Pola Konstruktor Aman untuk DependencyObjects
Umumnya, konstruktor kelas tidak boleh memanggil panggilan balik seperti metode virtual atau delegasi, karena konstruktor dapat disebut sebagai inisialisasi dasar konstruktor untuk kelas turunan. Memasukkan virtual mungkin dilakukan pada status inisialisasi yang tidak lengkap dari objek tertentu. Namun, sistem properti itu sendiri memanggil dan mengekspos panggilan balik secara internal, sebagai bagian dari sistem properti dependensi. Sesederhana operasi sebagai menetapkan nilai properti dependensi dengan SetValue panggilan berpotensi menyertakan panggilan balik di suatu tempat dalam penentuan. Untuk alasan ini, Anda harus berhati-hati saat mengatur nilai properti dependensi dalam isi konstruktor, yang dapat menjadi bermasalah jika jenis Anda digunakan sebagai kelas dasar. Ada pola tertentu untuk menerapkan DependencyObject konstruktor yang menghindari masalah tertentu dengan status properti dependensi dan panggilan balik yang melekat, yang didokumentasikan di sini.
Metode Virtual Sistem Properti
Metode virtual atau panggilan balik berikut berpotensi dipanggil selama komputasi SetValue panggilan yang menetapkan nilai properti dependensi: ValidateValueCallback, , PropertyChangedCallbackCoerceValueCallback, OnPropertyChanged. Masing-masing metode virtual atau panggilan balik ini melayani tujuan tertentu dalam memperluas fleksibilitas sistem properti Windows Presentation Foundation (WPF) dan properti dependensi. Untuk informasi selengkapnya tentang cara menggunakan virtual ini untuk menyesuaikan penentuan nilai properti, lihat Callback dan Validasi Properti Dependensi.
Penegakan Aturan FXCop vs. Virtual Sistem Properti
Jika Anda menggunakan alat Microsoft FXCop sebagai bagian dari proses build Anda, dan Anda berasal dari kelas kerangka kerja WPF tertentu yang memanggil konstruktor dasar, atau menerapkan properti dependensi Anda sendiri pada kelas turunan, Anda mungkin mengalami pelanggaran aturan FXCop tertentu. String nama untuk pelanggaran ini adalah:
DoNotCallOverridableMethodsInConstructors
Ini adalah aturan yang merupakan bagian dari aturan publik default yang ditetapkan untuk FXCop. Apa yang mungkin dilaporkan aturan ini adalah jejak melalui sistem properti dependensi yang akhirnya memanggil metode virtual sistem properti dependensi. Pelanggaran aturan ini mungkin terus muncul bahkan setelah mengikuti pola konstruktor yang direkomendasikan yang didokumenkan dalam topik ini, jadi Anda mungkin perlu menonaktifkan atau menekan aturan tersebut dalam konfigurasi seperangkat aturan FXCop Anda.
Sebagian besar masalah berasal dari kelas turunan, tidak menggunakan kelas yang ada
Masalah yang dilaporkan oleh aturan ini terjadi ketika kelas yang Anda terapkan dengan metode virtual dalam urutan konstruksinya kemudian berasal. Jika Anda menyegel kelas Anda, atau mengetahui atau memberlakukan bahwa kelas Anda tidak akan berasal, pertimbangan yang dijelaskan di sini dan masalah yang memotivasi aturan FXCop tidak berlaku untuk Anda. Namun, jika Anda menulis kelas sedih sehingga mereka dimaksudkan untuk digunakan sebagai kelas dasar, misalnya jika Anda membuat templat, atau kumpulan pustaka kontrol yang dapat diperluas, Anda harus mengikuti pola yang direkomendasikan di sini untuk konstruktor.
Konstruktor Default Harus Menginisialisasi Semua Nilai yang Diminta Oleh Panggilan Balik
Setiap anggota instans yang digunakan oleh kelas Anda mengambil alih atau panggilan balik (panggilan balik dari daftar di bagian Virtual Sistem Properti) harus diinisialisasi di konstruktor tanpa parameter kelas Anda, bahkan jika beberapa nilai tersebut diisi oleh nilai "nyata" melalui parameter konstruktor tanpa parameter.
Contoh kode berikut (dan contoh berikutnya) adalah contoh pseudo-C# yang melanggar aturan ini dan menjelaskan masalahnya:
public class MyClass : DependencyObject
{
public MyClass() {}
public MyClass(object toSetWobble)
: this()
{
Wobble = toSetWobble; //this is backed by a DependencyProperty
_myList = new ArrayList(); // this line should be in the default ctor
}
public static readonly DependencyProperty WobbleProperty =
DependencyProperty.Register("Wobble", typeof(object), typeof(MyClass));
public object Wobble
{
get { return GetValue(WobbleProperty); }
set { SetValue(WobbleProperty, value); }
}
protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e)
{
int count = _myList.Count; // null-reference exception
}
private ArrayList _myList;
}
Ketika kode aplikasi memanggil new MyClass(objectvalue)
, ini memanggil konstruktor tanpa parameter dan konstruktor kelas dasar. Kemudian mengatur Property1 = object1
, yang memanggil metode OnPropertyChanged
virtual pada pemilik MyClass
DependencyObject. Penimpaan _myList
mengacu pada , yang belum diinisialisasi.
Salah satu cara untuk menghindari masalah ini adalah dengan memastikan bahwa panggilan balik hanya menggunakan properti dependensi lain, dan bahwa setiap properti dependensi tersebut memiliki nilai default yang ditetapkan sebagai bagian dari metadata terdaftarnya.
Pola Konstruktor Aman
Untuk menghindari risiko inisialisasi yang tidak lengkap jika kelas Anda digunakan sebagai kelas dasar, ikuti pola-pola berikut:
Konstruktor tanpa parameter memanggil inisialisasi dasar
Terapkan konstruktor ini yang memanggil default dasar:
public MyClass : SomeBaseClass {
public MyClass() : base() {
// ALL class initialization, including initial defaults for
// possible values that other ctors specify or that callbacks need.
}
}
Konstruktor non-default (kenyamanan), tidak cocok dengan tanda tangan dasar apa pun
Jika konstruktor ini menggunakan parameter untuk mengatur properti dependensi dalam inisialisasi, pertama-tama panggil konstruktor tanpa parameter kelas Anda sendiri untuk inisialisasi, lalu gunakan parameter untuk mengatur properti dependensi. Ini bisa berupa properti dependensi yang ditentukan oleh kelas Anda, atau properti dependensi yang diwarisi dari kelas dasar, tetapi dalam kedua kasus, gunakan pola berikut:
public MyClass : SomeBaseClass {
public MyClass(object toSetProperty1) : this() {
// Class initialization NOT done by default.
// Then, set properties to values as passed in ctor parameters.
Property1 = toSetProperty1;
}
}
Konstruktor non-default (kenyamanan), yang cocok dengan tanda tangan dasar
Alih-alih memanggil konstruktor dasar dengan parameterisasi yang sama, panggil lagi konstruktor tanpa parameter kelas Anda sendiri. Jangan panggil penginisialisasi dasar; sebagai gantinya Anda harus memanggil this()
. Kemudian reproduksi perilaku konstruktor asli dengan menggunakan parameter yang diteruskan sebagai nilai untuk mengatur properti yang relevan. Gunakan dokumentasi konstruktor dasar asli untuk panduan dalam menentukan properti yang dimaksudkan untuk mengatur parameter tertentu:
public MyClass : SomeBaseClass {
public MyClass(object toSetProperty1) : this() {
// Class initialization NOT done by default.
// Then, set properties to values as passed in ctor parameters.
Property1 = toSetProperty1;
}
}
Harus cocok dengan semua tanda tangan
Untuk kasus di mana jenis dasar memiliki beberapa tanda tangan, Anda harus dengan sengaja mencocokkan semua kemungkinan tanda tangan dengan implementasi konstruktor Anda sendiri yang menggunakan pola yang direkomendasikan untuk memanggil konstruktor tanpa parameter kelas sebelum mengatur properti lebih lanjut.
Mengatur properti dependensi dengan SetValue
Pola yang sama ini berlaku jika Anda mengatur properti yang tidak memiliki pembungkus untuk kenyamanan pengaturan properti, dan mengatur nilai dengan SetValue. Panggilan Anda ke SetValue parameter konstruktor pass through tersebut juga harus memanggil konstruktor tanpa parameter kelas untuk inisialisasi.
Lihat juga
.NET Desktop feedback