C # 4.0 memperkenalkan tipe baru yang disebut 'dinamis'. Kedengarannya bagus, tapi untuk apa programmer menggunakannya?
Apakah ada situasi di mana itu bisa menyelamatkan hari?
C # 4.0 memperkenalkan tipe baru yang disebut 'dinamis'. Kedengarannya bagus, tapi untuk apa programmer menggunakannya?
Apakah ada situasi di mana itu bisa menyelamatkan hari?
Jawaban:
Kata kunci dinamis baru untuk C # 4.0, dan digunakan untuk memberi tahu kompiler bahwa tipe variabel dapat berubah atau tidak diketahui hingga runtime. Anggap saja mampu berinteraksi dengan Obyek tanpa harus melemparkannya.
dynamic cust = GetCustomer();
cust.FirstName = "foo"; // works as expected
cust.Process(); // works as expected
cust.MissingMethod(); // No method found!
Perhatikan bahwa kami tidak perlu memberikan atau mendeklarasikan pelanggan sebagai tipe Pelanggan. Karena kami menyatakannya dinamis, runtime mengambil alih, lalu mencari dan menetapkan properti FirstName untuk kami. Sekarang, tentu saja, ketika Anda menggunakan variabel dinamis, Anda berhenti memeriksa tipe kompiler. Ini berarti panggilan cust.MissingMethod () akan mengkompilasi dan tidak gagal sampai runtime. Hasil dari operasi ini adalah RuntimeBinderException karena MissingMethod tidak didefinisikan pada kelas Pelanggan.
Contoh di atas menunjukkan bagaimana dinamis bekerja saat memanggil metode dan properti. Fitur kuat lainnya (dan berpotensi berbahaya) adalah dapat menggunakan kembali variabel untuk berbagai jenis data. Saya yakin programmer Python, Ruby, dan Perl di luar sana dapat memikirkan sejuta cara untuk mengambil keuntungan dari ini, tapi saya telah menggunakan C # begitu lama sehingga hanya terasa "salah" bagi saya.
dynamic foo = 123;
foo = "bar";
OK, jadi Anda kemungkinan besar tidak akan sering menulis kode seperti di atas. Mungkin ada saat-saat ketika penggunaan kembali variabel bisa berguna atau membersihkan sepotong kode warisan. Satu kasus sederhana yang sering saya temui adalah terus-menerus harus memasukkan antara desimal dan ganda.
decimal foo = GetDecimalValue();
foo = foo / 2.5; // Does not compile
foo = Math.Sqrt(foo); // Does not compile
string bar = foo.ToString("c");
Baris kedua tidak dikompilasi karena 2,5 diketik sebagai ganda dan baris 3 tidak dikompilasi karena Math.Sqrt mengharapkan ganda. Jelas, yang harus Anda lakukan adalah melemparkan dan / atau mengubah tipe variabel Anda, tetapi mungkin ada situasi di mana dinamika masuk akal untuk digunakan.
dynamic foo = GetDecimalValue(); // still returns a decimal
foo = foo / 2.5; // The runtime takes care of this for us
foo = Math.Sqrt(foo); // Again, the DLR works its magic
string bar = foo.ToString("c");
Baca lebih banyak fitur: http://www.codeproject.com/KB/cs/CSharp4Features.aspx
dynamic
in c # untuk menyelesaikan masalah yang dapat diselesaikan (mungkin lebih baik) dengan fitur c # standar dan pengetikan statis, atau paling banyak dengan inferensi tipe ( var
). dynamic
seharusnya hanya digunakan ketika datang ke masalah interoperabilitas dengan DLR. Jika Anda menulis kode dalam bahasa yang diketik statis, seperti c #, maka lakukan, dan jangan meniru bahasa dinamis. Itu jelek.
dynamic
variabel dalam kode Anda di mana Anda tidak membutuhkannya (seperti dalam contoh Anda dengan squareroot) Anda menyerah memeriksa kesalahan waktu kompilasi bersih; sebaliknya Anda sekarang mendapatkan kemungkinan kesalahan runtime.
Kata dynamic
kunci ditambahkan, bersama dengan banyak fitur baru lainnya dari C # 4.0, untuk membuatnya lebih mudah untuk berbicara dengan kode yang hidup atau berasal dari runtimes lain, yang memiliki API berbeda.
Ambil sebuah contoh.
Jika Anda memiliki objek COM, seperti Word.Application
objek, dan ingin membuka dokumen, metode untuk melakukannya dilengkapi dengan tidak kurang dari 15 parameter, yang sebagian besar bersifat opsional.
Untuk memanggil metode ini, Anda perlu sesuatu seperti ini (saya menyederhanakan, ini bukan kode aktual):
object missing = System.Reflection.Missing.Value;
object fileName = "C:\\test.docx";
object readOnly = true;
wordApplication.Documents.Open(ref fileName, ref missing, ref readOnly,
ref missing, ref missing, ref missing, ref missing, ref missing,
ref missing, ref missing, ref missing, ref missing, ref missing,
ref missing, ref missing);
Catat semua argumen itu? Anda harus meneruskannya karena C # sebelum versi 4.0 tidak memiliki gagasan argumen opsional. Di C # 4.0, COM API telah dibuat lebih mudah untuk dikerjakan dengan memperkenalkan:
ref
opsional untuk API COMSintaks baru untuk panggilan di atas adalah:
wordApplication.Documents.Open(@"C:\Test.docx", ReadOnly: true);
Lihat seberapa mudah tampilannya, menjadi lebih mudah dibaca?
Mari kita pisahkan itu:
named argument, can skip the rest
|
v
wordApplication.Documents.Open(@"C:\Test.docx", ReadOnly: true);
^ ^
| |
notice no ref keyword, can pass
actual parameter values instead
Ajaibnya adalah bahwa kompiler C # sekarang akan menyuntikkan kode yang diperlukan, dan bekerja dengan kelas baru di runtime, untuk melakukan hal yang hampir sama persis seperti yang Anda lakukan sebelumnya, tetapi sintaks telah disembunyikan dari Anda, sekarang Anda dapat fokus pada apa , dan tidak begitu banyak tentang bagaimana . Anders Hejlsberg gemar mengatakan bahwa Anda harus memanggil "mantra" yang berbeda, yang merupakan semacam permainan kata-kata pada keajaiban semuanya, di mana Anda biasanya harus melambaikan tangan Anda dan mengucapkan beberapa kata ajaib dalam urutan yang benar. untuk mendapatkan jenis mantra tertentu. Cara API lama untuk berbicara dengan objek COM sangat banyak, Anda perlu melompati banyak rintangan untuk membujuk kompiler untuk mengkompilasi kode untuk Anda.
Hal-hal terurai dalam C # sebelum versi 4.0 bahkan lebih jika Anda mencoba untuk berbicara dengan objek COM yang Anda tidak memiliki antarmuka atau kelas untuk, semua yang Anda miliki adalah IDispatch
referensi.
Jika Anda tidak tahu apa itu, IDispatch
pada dasarnya refleksi untuk objek COM. Dengan sebuah IDispatch
antarmuka, Anda dapat menanyakan objek "apa nomor id untuk metode yang dikenal sebagai Simpan", dan membangun array dari jenis tertentu yang berisi nilai argumen, dan akhirnya memanggil Invoke
metode pada IDispatch
antarmuka untuk memanggil metode, melewati semua informasi yang berhasil Anda jelajahi bersama.
Metode Simpan di atas dapat terlihat seperti ini (ini jelas bukan kode yang tepat):
string[] methodNames = new[] { "Open" };
Guid IID = ...
int methodId = wordApplication.GetIDsOfNames(IID, methodNames, methodNames.Length, lcid, dispid);
SafeArray args = new SafeArray(new[] { fileName, missing, missing, .... });
wordApplication.Invoke(methodId, ... args, ...);
Semua ini hanya untuk membuka dokumen.
VB memiliki argumen opsional dan dukungan untuk sebagian besar dari ini di luar kotak sejak lama, jadi kode C # ini:
wordApplication.Documents.Open(@"C:\Test.docx", ReadOnly: true);
pada dasarnya hanya C # mengejar VB dalam hal ekspresi, tetapi melakukannya dengan cara yang benar, dengan membuatnya dapat diperpanjang, dan tidak hanya untuk COM. Tentu saja ini juga tersedia untuk VB.NET atau bahasa lain yang dibangun di atas runtime .NET.
Anda dapat menemukan informasi lebih lanjut tentang IDispatch
antarmuka di Wikipedia: IDispatch jika Anda ingin membaca lebih lanjut tentang itu. Benar-benar hal yang mengerikan.
Namun, bagaimana jika Anda ingin berbicara dengan objek Python? Ada API yang berbeda untuk itu daripada yang digunakan untuk objek COM, dan karena objek Python juga dinamis di alam, Anda perlu menggunakan sihir refleksi untuk menemukan metode yang tepat untuk memanggil, parameternya, dll. Tetapi bukan .NET refleksi, sesuatu yang ditulis untuk Python, sangat mirip dengan kode IDispatch di atas, sama sekali berbeda.
Dan untuk Ruby? API yang berbeda masih.
JavaScript? Kesepakatan yang sama, API yang berbeda untuk itu juga.
Kata kunci dinamis terdiri dari dua hal:
dynamic
dynamic
diperlukan kata kunci, dan memetakan panggilan ke cara yang benar dalam melakukan sesuatu. API bahkan didokumentasikan, jadi jika Anda memiliki objek yang berasal dari runtime yang tidak tercakup, Anda dapat menambahkannya.Namun, dynamic
kata kunci tidak dimaksudkan untuk mengganti kode .NET-only yang ada. Tentu, Anda bisa melakukannya, tetapi itu tidak ditambahkan karena alasan itu, dan penulis bahasa pemrograman C # dengan Anders Hejlsberg di depan, sangat bersikeras bahwa mereka masih menganggap C # sebagai bahasa yang diketik dengan kuat, dan tidak akan mengorbankan prinsip itu.
Ini berarti walaupun Anda dapat menulis kode seperti ini:
dynamic x = 10;
dynamic y = 3.14;
dynamic z = "test";
dynamic k = true;
dynamic l = x + y * z - k;
dan mengkompilasinya, itu tidak dimaksudkan sebagai semacam jenis sistem ajaib-mari-cari-cari-apa-yang-Anda-maksud-saat-runtime.
Seluruh tujuannya adalah untuk mempermudah berbicara dengan jenis objek lain.
Ada banyak materi di internet tentang kata kunci, pendukung, lawan, diskusi, kata-kata kasar, pujian, dll.
Saya sarankan Anda mulai dengan tautan berikut dan kemudian google untuk lebih:
dynamic
ditambahkan, untuk mendukung ekosistem lain untuk bagaimana doa metode seperti refleksi dapat dilakukan, serta memberikan semacam pendekatan kotak hitam untuk struktur data dengan cara terdokumentasi untuk mencapai ini.
Saya terkejut bahwa tidak ada yang menyebutkan pengiriman ganda . Cara biasa untuk mengatasinya adalah melalui pola Pengunjung dan itu tidak selalu memungkinkan sehingga Anda berakhir dengan is
cek bertumpuk .
Jadi, inilah contoh kehidupan nyata dari aplikasi saya sendiri. Alih-alih melakukan:
public static MapDtoBase CreateDto(ChartItem item)
{
if (item is ElevationPoint) return CreateDtoImpl((ElevationPoint)item);
if (item is MapPoint) return CreateDtoImpl((MapPoint)item);
if (item is MapPolyline) return CreateDtoImpl((MapPolyline)item);
//other subtypes follow
throw new ObjectNotFoundException("Counld not find suitable DTO for " + item.GetType());
}
Anda melakukannya:
public static MapDtoBase CreateDto(ChartItem item)
{
return CreateDtoImpl(item as dynamic);
}
private static MapDtoBase CreateDtoImpl(ChartItem item)
{
throw new ObjectNotFoundException("Counld not find suitable DTO for " + item.GetType());
}
private static MapDtoBase CreateDtoImpl(MapPoint item)
{
return new MapPointDto(item);
}
private static MapDtoBase CreateDtoImpl(ElevationPoint item)
{
return new ElevationDto(item);
}
Perhatikan bahwa dalam kasus pertama ElevationPoint
adalah subkelas MapPoint
dan jika tidak ditempatkan sebelum MapPoint
tidak akan pernah tercapai. Ini tidak terjadi dengan dinamis, karena metode pencocokan terdekat akan dipanggil.
Seperti yang mungkin Anda tebak dari kode, fitur itu berguna ketika saya melakukan terjemahan dari objek ChartItem ke versi serializable mereka. Saya tidak ingin mencemari kode saya dengan pengunjung dan saya tidak ingin juga mencemari ChartItem
objek saya dengan atribut spesifik serialisasi yang tidak berguna.
is
ditumpuk ditumpuk satu di atas yang lain.
magic
; tidak ada yang namanya sihir.
Itu membuatnya lebih mudah untuk bahasa yang diketik statis (CLR) untuk beroperasi dengan yang dinamis (python, ruby ...) berjalan di DLR (runtime bahasa dinamis), lihat MSDN :
Misalnya, Anda dapat menggunakan kode berikut untuk menambah penghitung dalam XML di C #.
Scriptobj.SetProperty("Count", ((int)GetProperty("Count")) + 1);
Dengan menggunakan DLR, Anda bisa menggunakan kode berikut sebagai ganti untuk operasi yang sama.
scriptobj.Count += 1;
MSDN mencantumkan keuntungan-keuntungan ini:
- Menyederhanakan Porting Bahasa Dinamis ke .NET Framework
- Mengaktifkan Fitur Dinamis dalam Bahasa yang Diketik Secara Statis
- Memberikan Manfaat DLR dan .NET Framework di Masa Depan
- Memungkinkan Berbagi Perpustakaan dan Objek
- Menyediakan Pengiriman dan Doa Dinamis Cepat
Lihat MSDN untuk detail lebih lanjut.
Contoh penggunaan:
Anda mengkonsumsi banyak kelas yang memiliki properti komun 'CreationDate':
public class Contact
{
// some properties
public DateTime CreationDate { get; set; }
}
public class Company
{
// some properties
public DateTime CreationDate { get; set; }
}
public class Opportunity
{
// some properties
public DateTime CreationDate { get; set; }
}
Jika Anda menulis metode komun yang mengambil nilai properti 'CreationDate', Anda harus menggunakan refleksi:
static DateTime RetrieveValueOfCreationDate(Object item)
{
return (DateTime)item.GetType().GetProperty("CreationDate").GetValue(item);
}
Dengan konsep 'dinamis', kode Anda jauh lebih elegan:
static DateTime RetrieveValueOfCreationDate(dynamic item)
{
return item.CreationDate;
}
Ini sebagian besar akan digunakan oleh korban RAD dan Python untuk menghancurkan kualitas kode, IntelliSense dan kompilasi deteksi waktu bug.
Ini mengevaluasi saat runtime, sehingga Anda dapat beralih jenis seperti yang Anda bisa dalam JavaScript ke apa pun yang Anda inginkan. Ini legit:
dynamic i = 12;
i = "text";
Jadi Anda dapat mengubah jenis yang Anda butuhkan. Gunakan itu sebagai pilihan terakhir; itu bermanfaat, tetapi saya mendengar banyak hal terjadi di bawah layar dalam hal IL yang dihasilkan dan itu bisa datang dengan harga kinerja.
Kasus penggunaan terbaik dari variabel tipe 'dinamis' untuk saya adalah ketika, baru-baru ini, saya menulis lapisan akses data di ADO.NET ( menggunakan SQLDataReader ) dan kode itu menggunakan prosedur tersimpan warisan yang sudah ditulis. Ada ratusan prosedur tersimpan warisan yang mengandung sebagian besar logika bisnis. Lapisan akses data saya diperlukan untuk mengembalikan semacam data terstruktur ke lapisan logika bisnis, berbasis C #, untuk melakukan beberapa manipulasi ( walaupun hampir tidak ada ). Setiap prosedur tersimpan mengembalikan set data yang berbeda ( kolom tabel ). Jadi alih-alih membuat lusinan kelas atau struct untuk menyimpan data yang dikembalikan dan meneruskannya ke BLL, saya menulis kode di bawah ini yang terlihat cukup elegan dan rapi.
public static dynamic GetSomeData(ParameterDTO dto)
{
dynamic result = null;
string SPName = "a_legacy_stored_procedure";
using (SqlConnection connection = new SqlConnection(DataConnection.ConnectionString))
{
SqlCommand command = new SqlCommand(SPName, connection);
command.CommandType = System.Data.CommandType.StoredProcedure;
command.Parameters.Add(new SqlParameter("@empid", dto.EmpID));
command.Parameters.Add(new SqlParameter("@deptid", dto.DeptID));
connection.Open();
using (SqlDataReader reader = command.ExecuteReader())
{
while (reader.Read())
{
dynamic row = new ExpandoObject();
row.EmpName = reader["EmpFullName"].ToString();
row.DeptName = reader["DeptName"].ToString();
row.AnotherColumn = reader["AnotherColumn"].ToString();
result = row;
}
}
}
return result;
}
dynamic np = Py.Import("numpy")
dynamic
ketika menerapkan operator numerik pada mereka. Ini memberikan keamanan jenis dan menghindari keterbatasan obat generik. Ini pada dasarnya * mengetik bebek:T y = x * (dynamic)x
dimana typeof(x) is T
Kasus penggunaan lain untuk dynamic
mengetik adalah untuk metode virtual yang mengalami masalah dengan kovarians atau contravariance. Salah satu contohnya adalah Clone
metode terkenal yang mengembalikan objek dengan tipe yang sama dengan objek yang dipanggil. Masalah ini tidak sepenuhnya diselesaikan dengan pengembalian dinamis karena melewati pemeriksaan tipe statis, tetapi setidaknya Anda tidak perlu menggunakan gips jelek setiap saat seperti saat menggunakan polos object
. Kalau tidak dikatakan, para pemain menjadi tersirat.
public class A
{
// attributes and constructor here
public virtual dynamic Clone()
{
var clone = new A();
// Do more cloning stuff here
return clone;
}
}
public class B : A
{
// more attributes and constructor here
public override dynamic Clone()
{
var clone = new B();
// Do more cloning stuff here
return clone;
}
}
public class Program
{
public static void Main()
{
A a = new A().Clone(); // No cast needed here
B b = new B().Clone(); // and here
// do more stuff with a and b
}
}