سؤال

عندما أعمل مع تطبيقات الواجهة الصريحة في C#، يصبح من الضروري في كثير من الأحيان إلقاء كائن على أحد واجهاته من أجل الوصول إلى عضو في تلك الواجهة. نظرًا لتحسين الموثوقية والقدرة على الصيانة التي يوفرها فحص نوع وقت الترجمة ، فقد فضلت دائمًا استخدام تحويلات ضمنية لأداء هذا. تتضمن الطريقة الوحيدة التي أعرفها القيام بذلك سطرين من التعليمات البرمجية وتقدم متغيرًا آخر في النطاق. أدناه مثال:

public interface IMyType
{
    string SayHello();
}

public class MyType : IMyType
{
    string IMyType.SayHello() { return "Hello!"; }
}

class Program
{
    static void Main(string[] args)
    {
        var item = new MyType();

        // Option 1 - Implicit cast. Compile time checked but takes two lines.
        IMyType item2 = item;
        System.Console.WriteLine(item2.SayHello());

        // Option 2 - One line but risks an InvalidCastException at runtime if MyType changes.
        System.Console.WriteLine(((IMyType)item).SayHello());

        // Option 3 - One line but risks a NullReferenceException at runtime if MyType changes.
        System.Console.WriteLine((item as IMyType).SayHello());
    }
}

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

سؤالي هو ثلاثة أضعاف:

  • أي من الخيارات المذكورة أعلاه تفضل (ولماذا)؟
  • هل هناك طريقة أفضل للقيام بذلك؟
  • ما هي بعض أفضل الممارسات المتعلقة بأداء القوالب الصريحة عندما تكون القوالب الضمنية ممكنة؟
هل كانت مفيدة؟

المحلول

فيما يلي فحص بطانة واحدة:

public static class Converter
{
    public static T ReturnAs<T>(T item)
    {
        return item;
    }
}


class Program
{
    static void Main(string[] args)
    {
        var item = new MyType();

        // Option 1 - Implicit cast. Compile time checked but takes two lines.
        IMyType item2 = item;
        System.Console.WriteLine(item2.SayHello());

        // Option 2 - One line but risks an InvalidCastException at runtime if MyType changes.
        System.Console.WriteLine(((IMyType)item).SayHello());

        // Option 3 - One line but risks a NullReferenceException at runtime if MyType changes.
        System.Console.WriteLine((item as IMyType).SayHello());

        // Option 4 - compile time one liner
        Converter.ReturnAs<IMyType>(item).SayHello();
    }
}

نصائح أخرى

كإجابة على جميع الأسئلة الثلاثة: الاعتماد على يلقي الضمني كقاعدة عامة. أنت تقوم برمجة ضد الواجهة ، وليس التنفيذ.

بالنسبة للآخر ، إذا كان عليك حقًا الاعتماد على البرمجة مقابل التنفيذ (فئة مشتقة محددة) ، فتأكد من إمكانية إلقاء الكائن من النوع قبل محاولة القيام بأي شيء به. شيء من هذا القبيل:

var IMyType item3 = item as MyConcreteType;
if(item3 != null) {
    item3.SayHello();
}

إذا لم تكن متأكدًا من أن الكائن هو مثيل للواجهة ، فقم بفحص AS/NULL. عادةً ما تقوم بإرجاع واجهة من استدعاء طريقة/دالة في هذه الحالة تقوم بتخزينها فقط في متغير بدون طاقم (على الرغم من أن الفحص الفارغ لا يزال ضروريًا).

عادة ما أحب ذلك على النحو التالي:

class Program
{
    static void Main(string[] args)
    {
        var item = new MyType();
        if( item is IMyType ){
          Console.WriteLine( (item as IMyType).SayHello() );
        }
        else { /* Do something here... */ }

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