声明は嘘、嘘、挑発であることが判明しました
しかし、挑戦が受け入れられたので、それはもはや問題ではありませんでした。
免責事項
. . . .
トレーニング
継承チェーンを作成します。簡単にするために、パラメーターなしのコンストラクターを使用します。コンストラクターでは、呼び出されたオブジェクトのタイプとIDに関する情報を表示します。
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();
ただし、プログラムは出力します。 この場合、次のトリックを使用できます。コンパイル時のタイプについて誰が知っていますか?コンパイラ。また、これらのタイプに基づいてメソッドのオーバーロードを選択します。また、汎用タイプとメソッドの場合、構築されたエンティティも生成します。したがって、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
基本型コンストラクターの呼び出し順序の変更
タイプAの内部で、ヘルパータイプを作成します。
protected class CtorHelper
{
private CtorHelper() { }
}
ここではセマンティクスのみが重要であるため、型コンストラクターをプライベートにすることは理にかなっています。インスタンス化は意味がありません。このタイプは、タイプAコンストラクターのオーバーロードとそれから派生したオーバーロードを区別することのみを目的としています。同じ理由で、タイプは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