سؤال

تحديث!

انظر تشريحي لجزء من المواصفات C# أدناه ؛ أعتقد أنني يجب أن أفتقد شيئًا ما أنا يبدو أن السلوك الذي أصفه في هذا السؤال ينتهك المواصفات بالفعل.

تحديث 2!

حسنًا ، على مزيد من التفكير ، وبناءً على بعض التعليقات ، أعتقد أنني أفهم الآن ما يجري. تشير الكلمات "نوع المصدر" في المواصفات إلى النوع الذي يتم تحويله من -- بمعنى آخر، Type2 في مثالي أدناه - مما يعني ببساطة أن المترجم قادر على تضييق المرشحين وصولاً إلى المشغلين المحددة (منذ ذلك الحين Type2 هو نوع المصدر لكليهما). ومع ذلك ، لا يمكن تضييق الخيارات أكثر. لذا فإن الكلمات الرئيسية في المواصفات (كما تنطبق على هذا السؤال) هي "نوع المصدر", ، والتي أساءت تفسيرها سابقًا (على ما أظن) أنها تعني "إعلان النوع".


السؤال الأصلي

قل أن هذه الأنواع محددة:

class Type0
{
    public string Value { get; private set; }

    public Type0(string value)
    {
        Value = value;
    }
}

class Type1 : Type0
{
    public Type1(string value) : base(value) { }

    public static implicit operator Type1(Type2 other)
    {
        return new Type1("Converted using Type1's operator.");
    }
}

class Type2 : Type0
{
    public Type2(string value) : base(value) { }

    public static implicit operator Type1(Type2 other)
    {
        return new Type1("Converted using Type2's operator.");
    }
}

ثم قل أنا أفعل هذا:

Type2 t2 = new Type2("B");
Type1 t1 = t2;

من الواضح أن هذا غامض ، لأنه ليس واضحًا من implicit يجب استخدام المشغل. سؤالي هو - بما أنني لا أستطيع رؤيته أي طريقة لحل هذا الغموض (ليس الأمر كما لو أنه يمكنني إجراء بعض الممثلين الصريحين لتوضيح الإصدار الذي أريده) ، ومع ذلك فإن تعريفات الفصل أعلاه تتجمع - لماذا يسمح المترجم بالمطابقة implicit المشغلين على الإطلاق؟


تشريح

حسنًا ، سأخطو على مقتطف من المواصفات C# التي نقلتها Hans Passant في محاولة لفهم هذا.

ابحث عن مجموعة الأنواع ، D ، والتي سيتم النظر في مشغلي التحويل المعرفة من قبل المستخدم. تتكون هذه المجموعة من S (إذا كانت S عبارة عن فئة أو بنية) ، فإن الفئات الأساسية لـ S (إذا كانت S عبارة عن فئة) ، و T (إذا كانت T فئة أو بنية).

نحن نتحول من Type2 (س) إلى Type1 (ر). لذلك يبدو أن هنا د سوف تشمل جميع الأنواع الثلاثة في المثال: Type0 (لأنها فئة قاعدة س), Type1 (ر) و Type2 (س).

ابحث عن مجموعة من مشغلي التحويل المعرفة من قبل المستخدم المعدلة ، وتتكون هذه المجموعة من مشغلي التحويل الضمني المعرفة من قبل المستخدم المعلنة من قبل الفئات أو الهياكل في D التي تتحول من نوع يشمل S إلى نوع يشمله T. ، التحويل غير محدد ويحدث خطأ في وقت الترجمة.

حسنًا ، لدينا مشغلان يرضيان هذه الشروط. أعلن الإصدار في Type1 يلبي المتطلبات لأن Type1 في داخل د ويتحول من Type2 (الذي يشمل بوضوح س) إلى Type1 (والتي من الواضح أنها شملها ر). الإصدار في Type2 ايضا يلبي المتطلبات لنفس الأسباب بالضبط. لذا ش يشمل كل من هؤلاء المشغلين.

أخيرًا ، فيما يتعلق بإيجاد "نوع المصدر" الأكثر تحديداً SX من المشغلين في ش:

إذا كان أي من المشغلين في U يتحول من S ، فإن SX هو S.

حاليا، على حد سواء المشغلين في ش تحويل من س - لذلك هذا يخبرني ذلك SX هو س.

لا يعني هذا أن ال Type2 يجب استخدام الإصدار؟

لكن انتظر! أنا محتار!

لم أستطع أن يكون لدي فقط مُعرف Type1نسخة من المشغل ، وفي هذه الحالة ، سيكون المرشح المتبقي الوحيد هو Type1نسخة ، وحتى الآن وفقا للمواصفات SX سيكون Type2؟ هذا يبدو وكأنه سيناريو محتمل يوضح فيه المواصفات شيئًا مستحيلًا (أي أن التحويل أعلن فيه Type2 يجب استخدامها عندما يكون في الواقع غير موجود).

هل كانت مفيدة؟

المحلول

لا نريد حقًا أن يكون خطأ في وقت الترجمة فقط لتحديد التحويلات التي قد تسبب الغموض. لنفترض أننا نغير النوع 0 لتخزين مزدوج ، ولسبب ما نريد تقديم تحويلات منفصلة إلى عدد صحيح موقّع ومكتمل غير موقعة.

class Type0
{
    public double Value { get; private set; }

    public Type0(double value)
    {
        Value = value;
    }

    public static implicit operator Int32(Type0 other)
    {
        return (Int32)other.Value;
    }

    public static implicit operator UInt32(Type0 other)
    {
        return (UInt32)Math.Abs(other.Value);
    }

}

هذا يجمع بشكل جيد ، ويمكنني استخدام كلا التحويلات مع

Type0 t = new Type0(0.9);
int i = t;
UInt32 u = t;

ومع ذلك ، فهو خطأ في التجميع في المحاولة float f = t لأنه يمكن استخدام أي من التحويلات الضمنية للوصول إلى نوع عدد صحيح يمكن تحويله بعد ذلك إلى تعويم.

نريد فقط أن يشتكي المترجم من هذه الغموض الأكثر تعقيدًا عند استخدامه بالفعل ، لأننا نرغب في تجميع النوع 6 أعلاه. من أجل الاتساق ، يجب أن يتسبب الغموض البسيط أيضًا في حدوث خطأ في النقطة التي تستخدمها بدلاً من تحديدها.

تعديل

نظرًا لأن Hans أزال إجابته التي نقلت المواصفات ، فإليك تشغيلًا سريعًا من خلال جزء من المواصفات C# التي تحدد ما إذا كان التحويل غامضًا ، بعد أن حددت u هي مجموعة من جميع التحويلات التي يمكن أن تؤدي المهمة:

  • ابحث عن نوع المصدر الأكثر تحديداً ، SX ، للمشغلين في U:
    • إذا كان أي من المشغلين في U يتحول من S ، فإن SX هو S.
    • بخلاف ذلك ، فإن SX هو النوع الأكثر شغفًا في المجموعة المشتركة من الأنواع المستهدفة للمشغلين في U. إذا لم يكن هناك أي نوع أكثر شمولاً ، فإن التحويل غامض ويحدث خطأ في وقت الترجمة.

معاد صياغة ، نفضل تحويل يتحول مباشرة من S ، وإلا فإننا نفضل النوع الذي "أسهل" لتحويل S. في كلا المثالين ، لدينا تحويلان من S متاح. إذا لم تكن هناك تحويلات من Type2, ، نفضل التحويل من Type0 أكثر من واحد من object. إذا لم يكن هناك نوع واحد هو الخيار الأفضل للتحويل منه ، فإننا نفشل هنا.

  • ابحث عن نوع الهدف الأكثر تحديدًا ، TX ، للمشغلين في U:
    • إذا كان أي من المشغلين في U يتحول إلى T ، فإن TX هو T.
    • بخلاف ذلك ، فإن TX هو النوع الأكثر شمولاً في المجموعة المشتركة من الأنواع المستهدفة للمشغلين في U. إذا لم يكن هناك أي نوع شامل ، فإن التحويل غامض ويحدث خطأ في وقت الترجمة.

مرة أخرى ، نفضل التحويل مباشرة إلى T ، لكننا سنستقر على النوع "الأسهل" لتحويل T. في مثال Dan ، لدينا تحويلان إلى T متاحان. في المثال الخاص بي ، الأهداف المحتملة هي Int32 و UInt32, ، ولا تطابق أفضل من الآخر ، لذلك هذا هو المكان الذي يفشل فيه التحويل. ليس لدى المترجم أي وسيلة لمعرفة ما إذا كان float f = t يعني float f = (float)(Int32)t أو float f = (float)(UInt32)t.

  • إذا كانت U تحتوي على مشغل تحويل واحد معرف من قبل المستخدم يتحول من SX إلى TX ، فهذا هو مشغل التحويل الأكثر تحديداً. في حالة عدم وجود مثل هذا المشغل ، أو إذا كان هناك أكثر من أحد المشغلات ، فإن التحويل غامض ويحدث خطأ في وقت الترجمة.

في مثال دان ، فشلنا هنا لأن لدينا تحويلان من SX إلى TX. لا يمكن أن يكون لدينا أي تحويلات من SX إلى TX إذا اخترنا تحويلات مختلفة عند تحديد SX و TX. على سبيل المثال ، إذا كان لدينا ملف Type1a مستمدة من Type1, ، ثم قد يكون لدينا تحويلات من Type2 إلى Type1a و من Type0 إلى Type1 هذه ستظل تمنحنا SX = Type2 و TX = Type1 ، لكن ليس لدينا بالفعل أي تحويل من Type2 إلى Type1. هذا جيد ، لأن هذا حقا غامض. لا يعرف برنامج التحويل البرمجي ما إذا كان سيتم تحويل Type2 إلى Type1a ثم يلقي إلى النوع 1 ، أو يلقي إلى النوع 0 أولاً حتى يتمكن من استخدام هذا التحويل إلى type1.

نصائح أخرى

في النهاية ، لا يمكن حظره بنجاح كامل. أنت وأنا يمكن أن ننشر مجموعتين. يمكن أن نبدأ في استخدام تجميعات بعضنا البعض ، مع تحديث خاصة بنا. ثم يمكننا تقديم كل من القوالب الضمنية بين الأنواع المحددة في كل مجموعة. فقط عندما ننشر الإصدار التالي ، يمكن القبض على هذا ، وليس في وقت الترجمة.

هناك ميزة في عدم محاولة حظر أشياء لا يمكن حظرها ، لأنها تجعل الوضوح والاتساق (وهناك درس للمشرعين في ذلك).

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top