Perangkap Antarmuka Delphi
Sekarang saya tahu dua jebakan utama:
Perangkap 1. Jenis Konversi Jenis Antarmuka
a) Anda tidak dapat memberikan referensi objek ke antarmuka yang tidak menyatakan jenis referensi ini, bahkan jika objek benar -benar mengimplementasikan antarmuka ini (hehe, keuntungannya sulit diucapkan). b) Ketika variabel objek ditetapkan ke variabel antarmuka dan ketika variabel antarmuka ditetapkan ke variabel objek, alamat variabel objek telah berubah, yaitu, itu bukan lagi objek asli, tetapi menunjuk ke alamat kesalahan kesalahan . Misalnya: I1 = Antarmuka
Fungsi lakukan: boolean;
akhir;
Tc1 = kelas
Att1: bilangan bulat;
akhir;
TC2 = kelas (TC1, I1)
Att2: integer;
Fungsi lakukan: boolean;
end; intf1: i1; obj1: tc !; obj2: tc2; obj2: = tc2.create;
OBJ1: = OBJ2.
I1 (obj2) .do;
I1 (OBJ1) .do; Karena tipe TC1 OBJ1 tidak menyatakan bahwa I1 diimplementasikan, itu tidak dapat dikonversi ke I1, bahkan jika OBJ1 menerapkan I1. Juga, jika Anda mengonversi objek menjadi antarmuka dan kemudian kembali, akan ada masalah. Obj2: = tc2.create; obj2.att1: = 0;
Intf1: = obj2; // benar. OBJ2: = intf1; OBJ2.ATT1: = 0; // Kesalahan akses alamat ilegal selama runtime. Artinya, setelah mengonversi dari referensi objek ke referensi pointer, alamat berubah, tetapi ketika referensi pointer diputar kembali dari referensi objek dan kemudian dikembalikan ke referensi objek (bug Delphi?).
Perangkap 2. Manajemen seumur hidup antarmuka
Berdasarkan akal sehat saya (di sini adalah pemrograman akal sehat, bukan akal penggunaan Delphi), saya pikir antarmuka tidak memerlukan manajemen seumur hidup karena antarmuka tidak dapat menghasilkan objek nyata sama sekali. Tetapi Delphi sekali lagi mencapai akal sehat saya (huh, mengapa Anda mengatakan "perbarui"?), Antarmuka memiliki seumur hidup, dan itu harus menerapkan tiga metode berikut: fungsi kueryInterface (const iid: tguid; keluar obj): hresult; stdcall; Jadi saya tidak tahu bagaimana menerapkan ketiga metode ini. J Jika Anda tidak ingin menerapkan ketiga metode ini sendiri, Anda dapat menggunakan tcomponent. Karena tcomponent telah menerapkan ketiga metode ini, dapat diwarisi darinya, jadi tidak perlu menerapkan ketiga metode ini. Bisakah Anda menggunakannya dengan percaya diri? Jawabannya adalah tidak. Karena Delphi diam -diam (karena di luar harapan saya) ketika Anda mengatur variabel antarmuka ke NIL. Function _Intfclear (Var Dest: iinterface): pointer; var p: pointer; ) ._Release; end; end; dan apa yang Anda lakukan saat _release? Fungsi tComponent._release: Integer; , maka tidak ada yang tidak akan melakukannya. Apa yang tidak kita kerjakan sebagai objek com, jadi tidak ada masalah? Jawabannya masih, pertimbangkan situasi berikut: obj2: = tc2.create; tryintf1: = obj2; intf1.do; Kesalahan akses alamat ilegal akan terjadi. Mengapa? Seperti disebutkan di atas, ketika referensi antarmuka diatur ke NIL, _Intfclear akan dipanggil, dan _intfclear akan memanggil objek _Release. Beberapa orang mengatakan itu tidak perlu? OBJ2: = TC2.Create; TryIntf1: = Obj2; Intf1.do; Akhirnya Obj2.Free; Akhir; Hasilnya mungkin masih tidak terduga, atau kesalahan akses alamat ilegal. Mengapa? Karena kompiler Delphi pintar, ia berpikir bahwa Anda lupa mengatur referensi alamat ini ke NIL, jadi Anda akan secara otomatis menambahkannya ke Anda. Bagaimana cara menyelesaikannya? Metode 1: Atur referensi antarmuka ke NIL terlebih dahulu, dan kemudian lepaskan objek. Intf1: = NIL; Pointer (intf1): = nil; Saya cenderung menggunakan metode kedua sehingga Anda tidak perlu berpikir tentang siapa yang akan dirilis terlebih dahulu. Selain itu, dalam beberapa pola desain, Anda hanya dapat memegang referensi antarmuka, dan Anda tidak tahu kapan objek yang direferensikan akan dirilis. Misalnya, pertimbangkan pola komposit. TComposite = class (tComponent, I1) Private Interlist: txContainer; // kelas kontainer yang menyimpan referensi antarmuka untuk "daun". PROSEDUR PUBLIK ADD (AINTF: I1); Jelas tidak, jadi akankah "daun" pasti dirilis lebih lambat dari objek "sintesis" ini? Saya kira tidak demikian. Jika peraturan ini diamanatkan, banyak fleksibilitas akan hilang. Jadi kami pasti berpikir bahwa ketika referensi antarmuka ini ditempatkan nol, tidak akan ada hubungan dengan objek asli, sehingga dapat menghindari kesalahan akses alamat ilegal setelah objek dirilis. Wadah apa yang harus Anda pertimbangkan untuk digunakan? Array? Daftar? Tinterfacelist? Pertama -tama, saya pikir itu pasti daftar tinterfacel, karena yang ingin kami akomodasi adalah antarmuka. Tetapi ketika gratis padanya, itu menempatkan semua antarmuka yang ditampung nil, yang persis seperti yang tidak kita inginkan. Atau kita dapat mengonversi referensi antarmuka yang disimpannya menjadi pointer sebelum gratis dan kemudian atur ke nihil. untuk i: = 0 untuk interlist.count -1 dopointer (interlist.items [i]): = nil; Lalu kami mencoba array. Interlist: Array I1; Dynamic Arrays tidak boleh dilepaskan. Tampaknya sangat berguna, tetapi ketika kompiler melepaskannya, ia masih akan mengatur setiap elemen ke NIL, dan sebagai antarmuka, masih ada kemungkinan kesalahan akses alamat ilegal. Ini dapat dilakukan dengan cara ini untuk i: = rendah (ARR) ke dopointer tinggi (ARR) (ARR [i]): = nil; tetapi ini merupakan pelanggaran kebiasaan pengkodean, dan Anda harus ingat untuk melakukannya setiap Waktu Anda menggunakannya, dan Anda mungkin tidak ingat untuk melakukannya atau tidak. Akhirnya, gunakan daftar. Namun, ada pointer di daftar, jadi saat menambahkan, Anda harus melanjutkan sebagai berikut: xxx.add (Aintf: i1) Mulai Interlist.add (pointer (Aintf)); Akhir; kebutuhan untuk pemrosesan khusus saat melepaskan. Tampaknya sempurna, tetapi masih ada jebakan. Interlist.add (tc2.create); atau obj2: = tc2.create; interlist.add (obj2); Karena itu adalah penunjuk murni, ketika dikonversi ke antarmuka, konversi alamat objek yang dirujuk ke referensi antarmuka tidak dilakukan (tidak tahu bagaimana melakukannya), jadi ketika memanggil metode yang dinyatakan oleh antarmuka, Ini adalah kesalahan akses alamat ilegal lainnya. Ini adalah satu -satunya cara untuk menulis: interlist.add (pointer (i1 (tc2.create)); Meskipun agak merepotkan, ini adalah solusi terbaik (seperti yang saya tahu). Karena jika Anda lupa bahwa transfer membuat kesalahan pada panggilan pertama, lebih mudah untuk menemukan kesalahan (dibandingkan dengan menggunakan array). Jadi saya sarankan: 1. Gunakan TList untuk mengelola referensi antarmuka. 2. Saat menambahkan, Anda harus mengubah objek menjadi antarmuka dan kemudian menjadi pointer. 3. Untuk referensi antarmuka yang tidak dikelola menggunakan TList. Untuk mengacu pada antarmuka, Anda harus secara manual diatur ke NIL dengan metode berikut: Pointer (IntFref): = NIL; Oleh karena itu, mewarisi darinya juga dapat menghemat kesulitan menerapkan ketiga metode ini. Tetapi _release diimplementasikan seperti ini: fungsi TinterfacedObject._release: Integer; objeknya. Beginilah cara diimplementasikan, ini membawa lebih banyak masalah daripada mewarisi dari tcomponent. Kecuali digunakan sebaliknya, itu tidak disarankan. Semua diskusi tentang antarmuka di atas terbatas pada antarmuka tingkat bahasa yang digunakan secara umum, dan tidak ada diskusi tentang antarmuka COM+. Implementasi antarmuka aneh Delphi ini sangat berkaitan dengan itu berkembang dari antarmuka COM+, yang merupakan hasil dari kompromi yang dibuat karena beban historis yang berat.