c# - تصويت - معنى فوت بالانجليزي




لماذا لا يتم ترجمة طريقة التمديد العامة هذه؟ (5)

المشكلة هي أن التباين لا يعمل إلا على أنواع المراجع أو تحويلات الهوية ، من المواصفات (القسم 13.1.3.2):

A type T<A1, …, An> is variance-convertible to a type T<B1, …, Bn> if T is either an interface or a delegate type declared with the variant type parameters T<X1, …, Xn>, and for each variant type parameter Xi one of the following holds:
•         Xi is covariant and an implicit reference or identity conversion exists from Ai to Bi
•         Xi is contravariant and an implicit reference or identity conversion exists from Bi to Ai
•         Xi is invariant and an identity conversion exists from Ai to Bi

لا يستطيع المترجم أن يتحقق من أن TFoo ليس بنية تقوم بتنفيذ IFoo<IChild> ، لذلك لا تجد طريقة الإضافة المطلوبة. لا يؤدي إضافة قيد class إلى DoSomething إلى حل المشكلة أيضًا ، لأن أنواع القيم لا تزال ترث من object ، وبالتالي تلبية القيد. IFoo<IChild> bar = foo; و DoSomethingElse(foo); كلاهما يعمل لأن لكل منهما IFoo<IChild> ضمنيًا من foo إلى IFoo<IChild> ، وهو نوع مرجع.

أطلب نفس السؤال الذي طرحه Mike Strobel في التعليقات أعلاه: لماذا لا تغير توقيع DoSomething من

public static void DoSomething<TFoo>(this TFoo foo)
        where TFoo : IFoo<IChild>

إلى

public static void DoSomething<TFoo>(this IFoo<IChild> foo)

يبدو أنك لا تكسب أي شيء عن طريق جعل هذه الطريقة عامة.

بعض المشاركات التي قرأتها حول هذا الموضوع:

أسلوب التمديد العام: لا يمكن الاستدلال على وسيطة النوع من الاستخدام

اريك ليبيرت - القيود ليست جزءا من التوقيع

C # نوع الجينات القيد

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

قل لدي هيكل الواجهة هذا:

public interface IBase {  }
public interface IChild : IBase {  }

public interface IFoo<out T> where T : IBase {  }

مع فئة طريقة الإضافات هذه المبنية حول الواجهات:

public static class FooExt
{
    public static void DoSomething<TFoo>(this TFoo foo)
        where TFoo : IFoo<IChild>
    {
        IFoo<IChild> bar = foo;

        //foo.DoSomethingElse();    // Doesn't compile -- why not?
        bar.DoSomethingElse();      // OK
        DoSomethingElse(foo);       // Also OK!
    }

    public static void DoSomethingElse(this IFoo<IBase> foo)
    {
    }
}

لماذا لا يتم ترجمة سطر DoSomething في DoSomething ؟ سيكون المترجم سعيدًا تمامًا للسماح لي بتعيين foo ، وهو من نفس نوع القيد العام ، واستدعاء طريقة الإضافة على ذلك بدلاً من ذلك. كما أنه لا توجد مشكلة في استدعاء طريقة ملحق بدون بناء جملة أسلوب ملحق.

هل يمكن لأي شخص تأكيد ما إذا كان هذا خطأ أو سلوكًا متوقعًا؟

شكر!

للاشارة فقط ، هنا هو خطأ الترجمة (أنواع مختصرة للشروط):

لا يحتوي 'TFoo' على تعريف لـ 'DoSomethingElse' ولديه أفضل أسلوب التحميل الزائد 'DoSomethingElse (IFoo)' يحتوي على بعض الوسائط غير الصحيحة


قطعة من التعليمات البرمجية الخاصة بك

public static void DoSomethingElse(this IFoo<IBase> foo)
{
}

يجعل DoSomethingElse متاحًا فقط في حالات IFoo<IBase> ، ومن الواضح أن foo ليس كذلك ، حيث إنه IFoo<IChild> . حقيقة أن IChild مشتقة من IBase لا تجعل IFoo<IChild> مشتقة من IFoo<IBase> . لذا لا يمكن اعتبار foo للأسف نوعًا من IFoo<IBase> ، ولذلك لا يمكن التذرع DoSomethingElse به.

ولكن يمكن تجنب هذه المشكلة بسهولة إذا قمت بتغيير طريقة الإضافة بشكل بسيط بهذه الطريقة:

public static void DoSomethingElse<T>(this IFoo<T> foo) where T : IBase
{
}

الآن يجمع كل شيء يعمل بشكل جيد.

الجزء الأكثر إثارة للاهتمام هو أن DoSomethingElse(foo); ترجمة عند استدعاء مع بناء جملة أسلوب ثابت ولكن ليس مع بناء جملة أسلوب ملحق. من الواضح أنه مع استدعاء أسلوب أسلوب ثابت ثابت ، يعمل التباين العام بشكل جيد: يتم كتابة الوسيطة foo كـ IFoo<IBase> ولكن يمكن تعيينها مع IFoo<IChild> ، ثم المكالمة على ما يرام. ولكن كطريقة تمديد ، بسبب الطريقة المعلنة ، يتم DoSomethingElse فقط في الحالات التي يتم كتابتها بشكل رسمي كـ IFoo<IBase> ، حتى لو كانت متوافقة مع IFoo<IChild> ، لذلك لا تعمل هذه الجملة على IFoo<IChild> حالات.


لا يتم تجميع السبب في أنه يشكو من "TFoo" لا يحتوي على تعريف لـ "DoSomethingElse"

لم يتم تعريف DoSomething على TFoo ولكن لـ IFoo<IBase> وبالتالي أيضًا لـ IFoo<IChild> .

فيما يلي بعض التغييرات التي قمت بها. إلقاء نظرة على ما المتغيرات ترجمة.

public interface IBase { }
public interface IChild : IBase { }

public interface IFoo<out T> where T : IBase { }

public static class FooExt
{
    public static void DoSomething<TFoo>(this TFoo foo)     where TFoo : IFoo<IChild>
    {
        IFoo<IChild> bar = foo;
        //Added by Ashwani 
        ((IFoo<IChild>)foo).DoSomethingElse();//Will Complie
        foo.DoSomethingElseTotally(); //Will Complie

        //foo.DoSomethingElse();    // Doesn't compile -- why not?
        bar.DoSomethingElse();      // OK
        DoSomethingElse(foo);       // Also OK!

    }

    public static void DoSomethingElse(this IFoo<IBase> foo)
    {
    }

    //Another method with is actually defined for <T>
    public static void DoSomethingElseTotally<T>(this T foo) 
    { 
    }

لذلك نأمل أن يكون ذلك أكثر إحساسًا ما يجمع وما ليس كذلك وليس خطأ برمجيًا.

HTH


لقد وجدت دليلا على أن هذا هو "علة".

على الرغم من أنه ليس من الضروري أن تدعم لغة CLR جميع الميزات المتوفرة في MSIL ، فإن الحقيقة هي أن ما تحاول القيام به صالح في MSIL.

إذا كنت تفكر في تفريغ الرمز إلى IL وجعل طريقة DoSomething تبدو كالتالي:

.method public hidebysig static void  DoSomething<(class TestLib.IFoo`1<class TestLib.IChild>) T>(!!T foo) cil managed
{
  .custom instance void [System.Core]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( 01 00 00 00 ) 
  // Code size       52 (0x34)
  .maxstack  1
  .locals init ([0] class TestLib.IFoo`1<class TestLib.IChild> bar)
  IL_0000:  nop
  IL_0001:  ldarg.0
  IL_0002:  box        !!T
  IL_0007:  call       void TestLib.Ext::DoSomethingElse(class TestLib.IFoo`1<class  TestLib.IBase>)
  IL_000c:  nop
  IL_000d:  ret
} // end of method Ext::DoSomething

كنت تكتشف أن هذا يجمع. وماذا عاكس حل هذا كما هو الحال في C #؟

public static void DoSomething<T>(this T foo) where T: IFoo<IChild>
{
    foo.DoSomethingElse();
}

يمكنك تحديد DoSomethingElse في IFoo ؟

public interface IFoo<out T> where T : IBase
{
    void DoSomethingElse();
}

تحديث

ربما يمكنك بعد ذلك تغيير التوقيع

public static void DoSomethingElse(this IFoo<IBase> foo)
=>
public static void DoSomethingElse<TFoo>(this TFoo foo) 
    where TFoo : IFoo<IChild>




covariance