واتضح أن البيان كذب وكذب واستفزاز
لكن لم يعد الأمر مهمًا ، لأنه تم قبول التحدي.
تنصل
. . . .
تدريب
نخلق سلسلة وراثية. من أجل البساطة ، سوف نستخدم منشئات بدون معلمات. في المُنشئ ، سنعرض معلومات حول نوع ومعرف الكائن الذي تم استدعاؤه عليه.
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
السذج الساذج يعتقد أن المترجم قد خدع
فهم النتيجة
بإضافة طريقة إلى الامتدادات :
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