c# معنى - لماذا لا يتم ترجمة طريقة التمديد العامة هذه؟




فوت بالانجليزي (7)

المشكلة هي أن التباين لا يعمل إلا على أنواع المراجع أو تحويلات الهوية ، من المواصفات (القسم 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> حالات.


لا أعرف لماذا لا يتم تجميعها ، لكن هل هذا بديل مقبول؟

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

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

على الرغم من أنه ليس من الضروري أن تدعم لغة 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>

نقلا عن مواصفات C #:

7.6.5.2 استدعاءات طريقة الإرشاد

في استدعاء الأسلوب (الفقرة 5.4.5.) من أحد النماذج

expr. معرف ()

expr. معرف (args)

expr. المعرّف <typeargs> ()

expr. المعرّف <typeargs> (args)

إذا لم تعثر المعالجة الاعتيادية للاحتجاج على أي طرق قابلة للتطبيق ، يتم إجراء محاولة لمعالجة البناء كإستدعاء طريقة ملحق. إذا كان expr أو أي من args يحتوي على ديناميكية وقت التحويل البرمجي ، فلن يتم تطبيق طرق الإضافات.

الهدف هو العثور على أفضل نوع من اسم C ، بحيث يمكن أن يحدث استدعاء الأسلوب الثابت المقابل:

ج. معرف (expr)

ج. معرف (expr ، args)

ج. المعرّف <typeargs> (expr)

ج. المعرّف <typeargs> (expr، args)

طريقة التمديد Ci.Mj مؤهلة إذا:

· Ci هي فئة غير عامة وغير متداخلة

· اسم Mj هو المعرف

· Mj يمكن الوصول إليها وقابلة للتطبيق عند تطبيقها على الحجج كطريقة ثابتة كما هو مبين أعلاه

· توجد هوية ضمنية أو مرجعية أو تحويل للملاكمة من expr إلى نوع المعلمة الأولى من Mj .

بما أن DoSomethingElse(foo) foo.DoSomethingElse() لكن foo.DoSomethingElse() لا ، يبدو وكأنه خطأ برمجي في دقة الحمولة IFoo<IBase> التمديد: يوجد تحويل مرجع ضمني من foo إلى IFoo<IBase> .


اختصار string.Format:

public static class StringExtensions
{
    // Enable quick and more natural string.Format calls
    public static string F(this string s, params object[] args)
    {
        return string.Format(s, args);
    }
}

مثال:

var s = "The co-ordinate is ({0}, {1})".F(point.X, point.Y);

للنسخ واللصق السريع ، انتقل here .

لا تجد أنه أكثر طبيعية لكتابة "some string".F("param") بدلاً من string.Format("some string", "param") ؟

للحصول على اسم أكثر قابلية للقراءة ، جرِّب أحد هذه الاقتراحات:

s = "Hello {0} world {1}!".Fmt("Stack", "Overflow");
s = "Hello {0} world {1}!".FormatBy("Stack", "Overflow");
s = "Hello {0} world {1}!".FormatWith("Stack", "Overflow");
s = "Hello {0} world {1}!".Display("Stack", "Overflow");
s = "Hello {0} world {1}!".With("Stack", "Overflow");

..





c# generics .net-4.0 extension-methods covariance