استدعاء مُنشئ النوع الأساسي في أي مكان

أجريت مقابلة مؤخرًا ، وكان هناك سؤال من بين أمور أخرى حول ترتيب استدعاء المُنشئين في C #. بعد الإجابة ، قرر الشخص الذي تمت مقابلته إظهار سعة الاطلاع وذكر أنه في Java ، يمكن استدعاء مُنشئ النوع الأساسي في أي مكان في المُنشئ من النوع المشتق ، و C # ، بالطبع ، يخسر في هذا.



واتضح أن البيان كذب وكذب واستفزاز
image



لكن لم يعد الأمر مهمًا ، لأنه تم قبول التحدي.



صورة



تنصل
. . . .



تدريب



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



public class A
{
    public A()
    {
        Console.WriteLine($"Type '{nameof(A)}' .ctor called on object #{GetHashCode()}");
    }
}

public class B : A
{
    public B()
    {
        Console.WriteLine($"Type '{nameof(B)}' .ctor called on object #{GetHashCode()}");
    }
}

public class C : B
{
    public C()
    {
        Console.WriteLine($"Type '{nameof(C)}' .ctor called on object #{GetHashCode()}");
    }
}


تشغيل البرنامج:



class Program
{
    static void Main()
    {
        new C();
    }
}


ونحصل على الناتج:



Type 'A' .ctor called on object #58225482

Type 'B' .ctor called on object #58225482

Type 'C' .ctor called on object #58225482



استطرادا غنائي
, . , . , . :



public A() : this() { } // CS0516  Constructor 'A.A()' cannot call itself


:



public A() : this(new object()) { }
public A(object _) : this(0) { }
public A(int _) : this() { } // CS0768  Constructor 'A.A(int)' cannot call itself through another constructor


إزالة الكود المكرر



أضف فئة مساعد:



internal static class Extensions
{
    public static void Trace(this object obj) =>
        Console.WriteLine($"Type '{obj.GetType().Name}' .ctor called on object #{obj.GetHashCode()}");
}


ونستبدل في جميع الصانعين



Console.WriteLine($"Type '{nameof(...)}' .ctor called on object #{GetHashCode()}");


على



this.Trace();


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



Type 'C' .ctor called on object #58225482

Type 'C' .ctor called on object #58225482

Type 'C' .ctor called on object #58225482







public static void Trace<T>(this T obj) =>
    Console.WriteLine($"Type '{typeof(T).Name}' .ctor called on object #{obj.GetHashCode()}");


الوصول إلى مُنشئ النوع الأساسي



هذا هو المكان الذي يأتي فيه التفكير للإنقاذ. أضف طريقة إلى الإضافات :



public static Action GetBaseConstructor<T>(this T obj) =>
    () => typeof(T)
          .BaseType
          .GetConstructor(Type.EmptyTypes)
          .Invoke(obj, Array.Empty<object>());


أضف الخاصية إلى النوعين B و C :



private Action @base => this.GetBaseConstructor();


استدعاء منشئ النوع الأساسي في أي مكان



قم بتغيير محتويات المنشئين B و C إلى:



this.Trace();
@base();


الآن يبدو الإخراج كما يلي:



Type 'A' .ctor called on object #58225482

Type 'B' .ctor called on object #58225482

Type 'A' .ctor called on object #58225482

Type 'C' .ctor called on object #58225482

Type 'A' .ctor called on object #58225482

Type 'B' .ctor called on object #58225482

Type 'A' .ctor called on object #58225482



تغيير ترتيب استدعاء منشئي النوع الأساسي



داخل النوع أ ، أنشئ نوعًا مساعدًا:



protected class CtorHelper
{
    private CtorHelper() { }
}


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



أضف المنشئات المناسبة إلى A و B و C :



protected A(CtorHelper _) { }
protected B(CtorHelper _) { }
protected C(CtorHelper _) { }


بالنسبة إلى النوعين B و C ، أضف استدعاء لجميع المنشئات:



: base(null)


نتيجة لذلك ، يجب أن تبدو الفصول هكذا
internal static class Extensions
{
    public static Action GetBaseConstructor<T>(this T obj) =>
        () => typeof(T)
        .BaseType
        .GetConstructor(Type.EmptyTypes)
        .Invoke(obj, Array.Empty<object>());

    public static void Trace<T>(this T obj) =>
        Console.WriteLine($"Type '{typeof(T).Name}' .ctor called on object #{obj.GetHashCode()}");
}

public class A
{
    protected A(CtorHelper _) { }

    public A()
    {
        this.Trace();
    }

    protected class CtorHelper
    {
        private CtorHelper() { }
    }
}

public class B : A
{
    private Action @base => this.GetBaseConstructor();

    protected B(CtorHelper _) : base(null) { }

    public B() : base(null)
    {
        this.Trace();
        @base();
    }
}

public class C : B
{
    private Action @base => this.GetBaseConstructor();

    protected C(CtorHelper _) : base(null) { }

    public C() : base(null)
    {
        this.Trace();
        @base();
    }
}


ويصبح الناتج:



Type 'C' .ctor called on object #58225482

Type 'B' .ctor called on object #58225482

Type 'A' .ctor called on object #58225482



السذج الساذج يعتقد أن المترجم قد خدع
image



فهم النتيجة



بإضافة طريقة إلى الامتدادات :



public static void TraceSurrogate<T>(this T obj) =>
    Console.WriteLine($"Type '{typeof(T).Name}' surrogate .ctor called on object #{obj.GetHashCode()}");


واستدعائها في جميع المنشئات التي تقبل CtorHelper ، نحصل على المخرجات التالية : ترتيب المنشئات وفقًا للمبدأ الأساسي / المشتق ، بالطبع ، لم يتغير. ولكن مع ذلك ، تم تغيير ترتيب المُنشئين المتاحين لكود العميل الذي يحمل الحمل الدلالي بفضل إعادة التوجيه من خلال المكالمات الموجهة إلى المنشئات المساعدة التي يتعذر على العميل الوصول إليها.



Type 'A' surrogate .ctor called on object #58225482

Type 'B' surrogate .ctor called on object #58225482

Type 'C' .ctor called on object #58225482

Type 'A' surrogate .ctor called on object #58225482

Type 'B' .ctor called on object #58225482

Type 'A' .ctor called on object #58225482






All Articles