Pernyataan itu ternyata bohong, bohong, dan provokasi
Tapi sudah tidak masalah lagi, karena tantangan diterima.
Penolakan
. . . .
Latihan
Kami membuat rantai warisan. Untuk mempermudah, kita akan menggunakan konstruktor tanpa parameter. Dalam konstruktor, kita akan menampilkan informasi tentang tipe dan pengenal dari objek yang dipanggil.
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()}");
}
}
Jalankan programnya:
class Program
{
static void Main()
{
new C();
}
}
Dan kami mendapatkan hasilnya:
Type 'A' .ctor called on object #58225482
Type 'B' .ctor called on object #58225482
Type 'C' .ctor called on object #58225482
Penyimpangan lirik
, . , . , . :
:
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
Menghapus kode duplikat
Tambahkan kelas pembantu:
internal static class Extensions
{
public static void Trace(this object obj) =>
Console.WriteLine($"Type '{obj.GetType().Name}' .ctor called on object #{obj.GetHashCode()}");
}
Dan kami mengganti di semua konstruktor
Console.WriteLine($"Type '{nameof(...)}' .ctor called on object #{GetHashCode()}");
di
this.Trace();
Namun, program sekarang mengeluarkan: Dalam kasus kami, trik berikut dapat digunakan. Siapa yang tahu tentang jenis waktu kompilasi? Penyusun. Ini juga memilih kelebihan metode berdasarkan jenis ini. Dan untuk tipe dan metode generik, ia juga menghasilkan entitas yang dibangun. Oleh karena itu, kami mengembalikan inferensi tipe yang benar dengan menulis ulang metode Trace sebagai berikut:
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()}");
Mengakses konstruktor tipe dasar
Di sinilah refleksi datang untuk menyelamatkan. Tambahkan metode ke Ekstensi :
public static Action GetBaseConstructor<T>(this T obj) =>
() => typeof(T)
.BaseType
.GetConstructor(Type.EmptyTypes)
.Invoke(obj, Array.Empty<object>());
Tambahkan properti ke tipe B dan C :
private Action @base => this.GetBaseConstructor();
Memanggil konstruktor tipe dasar di mana saja
Ubah konten konstruktor B dan C menjadi:
this.Trace();
@base();
Sekarang hasilnya terlihat seperti ini:
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
Mengubah Urutan Pemanggil Tipe Konstruktor Dasar
Di dalam tipe A, buat tipe pembantu:
protected class CtorHelper
{
private CtorHelper() { }
}
Karena hanya semantik yang penting di sini, masuk akal untuk menjadikan konstruktor tipe privat. Instansiasi tidak masuk akal. Jenis ini dimaksudkan hanya untuk membedakan antara beban berlebih dari konstruktor tipe A dan yang diturunkan darinya. Untuk alasan yang sama, tipe harus ditempatkan di dalam A dan dibuat terlindungi.
Tambahkan konstruktor yang sesuai ke A , B dan C :
protected A(CtorHelper _) { }
protected B(CtorHelper _) { }
protected C(CtorHelper _) { }
Untuk tipe B dan C, tambahkan panggilan ke semua konstruktor:
: base(null)
Akibatnya, kelas akan terlihat seperti ini
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();
}
}
Dan hasilnya menjadi:
Type 'C' .ctor called on object #58225482
Type 'B' .ctor called on object #58225482
Type 'A' .ctor called on object #58225482
Orang bodoh yang naif berpikir bahwa kompilator telah curang
Memahami hasilnya
Dengan menambahkan metode ke Ekstensi :
public static void TraceSurrogate<T>(this T obj) =>
Console.WriteLine($"Type '{typeof(T).Name}' surrogate .ctor called on object #{obj.GetHashCode()}");
dan memanggilnya di semua konstruktor yang menerima CtorHelper , kita mendapatkan keluaran berikut : Urutan konstruktor menurut prinsip dasar / turunan, tentu saja, tidak berubah. Tapi tetap saja, urutan konstruktor yang tersedia untuk kode klien yang membawa beban semantik berubah karena pengalihan melalui panggilan ke konstruktor helper yang tidak dapat diakses oleh klien dan tidak melakukan apa pun.
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