C # 4.0, 'dinamik' adlı yeni bir tür tanıttı. Her şey kulağa hoş geliyor, ama bir programcı bunu ne için kullanır?
Günü kurtarabilecek bir durum var mı?
C # 4.0, 'dinamik' adlı yeni bir tür tanıttı. Her şey kulağa hoş geliyor, ama bir programcı bunu ne için kullanır?
Günü kurtarabilecek bir durum var mı?
Yanıtlar:
Dinamik anahtar kelime C # 4.0 için yenidir ve derleyiciye bir değişkenin türünün değişebileceğini veya çalışma zamanına kadar bilinmediğini söylemek için kullanılır. Bir Nesne ile onu dökmek zorunda kalmadan etkileşime girebileceğini düşünün.
dynamic cust = GetCustomer();
cust.FirstName = "foo"; // works as expected
cust.Process(); // works as expected
cust.MissingMethod(); // No method found!
Tür Müşteri olarak döküm yapmamıza veya müşteri ilan etmemize gerek olmadığına dikkat edin. Dinamik olarak bildirdiğimiz için çalışma zamanı devralır ve FirstName özelliğini arar ve ayarlar. Elbette, dinamik bir değişken kullanırken derleyici türü denetiminden vazgeçiyorsunuz. Bu, cust.MissingMethod () çağrısının derleneceği ve çalışma zamanına kadar başarısız olmadığı anlamına gelir. MissingMethod Customer sınıfında tanımlanmadığından, bu işlemin sonucu bir RuntimeBinderException özelliğidir.
Yukarıdaki örnek, yöntemleri ve özellikleri çağırırken dinamiklerin nasıl çalıştığını gösterir. Bir başka güçlü (ve potansiyel olarak tehlikeli) özellik, farklı veri türleri için değişkenleri yeniden kullanabilmektir. Eminim Python, Ruby ve Perl programcıları bundan faydalanmak için milyonlarca yol düşünebilirler, ama ben C # 'yi çok uzun zamandır kullanıyorum.
dynamic foo = 123;
foo = "bar";
Tamam, bu yüzden büyük olasılıkla yukarıdaki gibi kod yazmayacaksınız. Bununla birlikte, değişken yeniden kullanımın kullanışlı olabileceği veya kirli bir eski kod parçasını temizleyebileceği zamanlar olabilir. Sık karşılaştığım basit bir durum, sürekli olarak ondalık ve çift arasında olmak zorunda.
decimal foo = GetDecimalValue();
foo = foo / 2.5; // Does not compile
foo = Math.Sqrt(foo); // Does not compile
string bar = foo.ToString("c");
İkinci satır derlenmez çünkü 2.5 çift olarak yazılır ve satır 3 derlenmez çünkü Math.Sqrt bir çift bekler. Açıkçası, yapmanız gereken tek şey değişken türünüzü yayınlamak ve / veya değiştirmek, ancak dinamik kullanımın mantıklı olduğu durumlar olabilir.
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");
Daha fazla bilgi okuyun: http://www.codeproject.com/KB/cs/CSharp4Features.aspx
dynamic
standart c # özellikleri ve statik yazarak ya da en çok tür çıkarım ( var
) ile çözülebilir (belki daha iyi) sorunları çözmek için c # kullanma düşüncesini sevmiyorum . dynamic
gerektiğini ancak bunun DLR ile interoperabilty konulara gelince kullanılabilir. C # gibi statik yazılan bir dilde kod yazarsanız, bunu yapın ve dinamik bir dil taklit etmeyin. Bu sadece çirkin.
dynamic
Kodunuzda değişkenlere ihtiyaç duymadığınız durumlarda (kare köklü örnekte olduğu gibi) çok fazla değişken kullanırsanız, derleme zamanı hata denetiminin temiz olmasını sağlarsınız; bunun yerine artık olası çalışma zamanı hataları alıyorsunuz.
dynamic
Anahtar kelime daha basit kod konuşmak yapmak, birlikte C # 4.0 diğer birçok yeni özellikler ile, eklendiğini yaşıyor veya farklı API'leri vardır diğer çalışma zamanları geliyor.
Bir örnek ver.
Nesne gibi bir COM nesneniz varsa Word.Application
ve bir belge açmak istiyorsanız, bunu yapmak için kullanılan yöntem, çoğu isteğe bağlı olan en az 15 parametre ile birlikte gelir.
Bu yöntemi çağırmak için, böyle bir şeye ihtiyacınız olacak (basitleştiriyorum, bu gerçek kod değil):
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);
Tüm bu argümanları not ettiniz mi? Sürüm 4.0 isteğe bağlı bağımsız değişkenler kavramı yoktu önce C # geçmesi gerekir. C # 4.0'da, COM API'lerinin aşağıdakilerle birlikte çalışması daha kolay hale getirilmiştir:
ref
COM API'leri için isteğe bağlı yapmaYukarıdaki çağrı için yeni sözdizimi şöyle olacaktır:
wordApplication.Documents.Open(@"C:\Test.docx", ReadOnly: true);
Ne kadar kolay göründüğüne, ne kadar okunabilir hale geldiğine bakın?
Bunu ayıralım:
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
Sihir, C # derleyicisinin artık gerekli kodu enjekte edeceği ve çalışma zamanında yeni sınıflarla çalışacağı, daha önce yaptığınızla aynı şeyi yapmasıdır, ancak sözdizimi sizden gizlenmiştir, şimdi ne ve nasıl çok fazla değil . Anders Hejlsberg, tipik olarak el (ler) inizi dalgalandırmak ve doğru sıralarda bazı sihirli kelimeler söylemek zorunda olduğunuz, her şeyin büyüsü üzerine bir çeşit kelime olan farklı "büyüler" çağırmanız gerektiğini söylemekten hoşlanıyor. belirli bir büyünün devam etmesini sağlamak. COM nesneleri ile konuşmak için eski API yolu çok şeydi, derleyici sizin için kodu derlemek için koaksiyel yapmak için bir sürü çember atlamak gerekiyordu.
Arabirim veya sınıfınız olmayan bir COM nesnesiyle konuşmaya çalışırsanız, sürüm 4.0'dan önce C # 'da işler daha da bozulur, sahip olduğunuz tek şey bir IDispatch
referanstır.
Ne olduğunu bilmiyorsanız IDispatch
, temelde COM nesneleri için yansımasıdır. Bir IDispatch
arabirim ile nesneye "Kaydet olarak bilinen yöntem için kimlik numarası nedir?" Sorusunu sorabilir ve bağımsız değişken değerlerini içeren belirli bir türde diziler oluşturabilir ve son Invoke
olarak IDispatch
arabirimi, yöntemi geçirerek, birlikte taramayı başardığınız bilgiler.
Yukarıdaki Kaydetme yöntemi şöyle görünebilir (bu kesinlikle doğru kod değildir):
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, ...);
Bütün bunlar sadece bir belge açmak için.
VB, uzun bir süre önce kutunun dışında bunun için isteğe bağlı argümanlar ve destek aldı, bu yüzden bu C # kodu:
wordApplication.Documents.Open(@"C:\Test.docx", ReadOnly: true);
temelde sadece C # ifadesi açısından VB'yi yakalamaktır, ancak sadece COM için değil, genişletilebilir hale getirerek doğru şekilde yapmaktır. Tabii ki bu VB.NET veya .NET çalışma zamanının üzerine inşa edilmiş herhangi bir dil için de kullanılabilir.
Hakkında daha fazla bilgi bulabilirsiniz. IDispatch
Arayüz için Wikipedia: IDispatch . Gerçekten kanlı şeyler.
Peki, bir Python nesnesiyle konuşmak isteseydiniz ne olurdu? Bunun için COM nesneleri için kullanılandan farklı bir API var ve Python nesneleri de doğada dinamik olduğundan, aramak için doğru yöntemleri, parametrelerini vb. Bulmak için yansıma sihrine başvurmanız gerekir. yansıma, Python için yazılmış bir şey, hemen hemen yukarıdaki IDispatch kodu gibi, tamamen farklı.
Ya Ruby için? Farklı bir API hala.
JavaScript? Aynı anlaşma, bunun için farklı API.
Dinamik anahtar kelime iki şeyden oluşur:
dynamic
dynamic
anahtar kelimenin gerektirdiği belirli bir API'yi uygulayan ve çağrıları doğru şekilde yapmak için eşleyen bir dizi çalışma zamanı sınıfı . API belgelenmiştir, bu nedenle kapsam dışında kalan bir çalışma zamanından gelen nesneleriniz varsa ekleyebilirsiniz.dynamic
Anahtar kelime, ancak, varolan herhangi bir .NET okunur kodunu değiştirmek anlamına gelmez. Tabii, bunu yapabilirsiniz , ancak bu nedenle eklenmedi ve C # programlama dilinin önünde Anders Hejlsberg ile yazarları, hala C #'ı güçlü bir şekilde yazılmış bir dil olarak gördükleri konusunda en kararlılardı ve feda etmeyecekler bu ilke.
Bu, şöyle bir kod yazabilmenize rağmen:
dynamic x = 10;
dynamic y = 3.14;
dynamic z = "test";
dynamic k = true;
dynamic l = x + y * z - k;
ve derleme, bir tür sihirli-hadi-anlamaya-ne demek istediğini-çalışma zamanı türünde bir sistem demek değildi.
Tüm amaç, diğer nesne türleri ile konuşmayı kolaylaştırmaktı.
İnternette anahtar kelime, destekçiler, rakipler, tartışmalar, rantlar, övgü, vb. Hakkında bol miktarda malzeme var.
Aşağıdaki bağlantılarla başlamanızı ve ardından daha fazla bilgi için google'ı kullanmanızı öneririm:
dynamic
, yansıma benzeri yöntem çağırma işleminin nasıl yapılabileceği konusunda diğer ekosistemleri desteklemek ve ayrıca veri yapılarına bunu başarmak için belgelenmiş bir yolla bir tür kara kutu yaklaşımı sağlamak için eklenmiştir.
Kimsenin birden fazla sevkiyattan bahsetmediğine şaşırdım . Bu sorunu çözmenin olağan yolu Ziyaretçi kalıbıdır ve bu her zaman mümkün değildir, bu nedenle yığılmış is
kontrollerle sonuçlanırsınız .
İşte size kendi uygulamamın gerçek bir örneği. Yapmak yerine:
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());
}
Siz yapıyorsunuz:
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);
}
İlk durumda ElevationPoint
alt sınıf olduğunu MapPoint
ve daha önce yerleştirilmemişse MapPoint
. En yakın eşleştirme yöntemi çağrılacağından, bu dinamik durum için geçerli değildir.
Koddan tahmin edebileceğiniz gibi, ChartItem nesnelerinden serileştirilebilir sürümlerine çeviri yaparken bu özellik kullanışlı oldu. Kodumu ziyaretçilerle kirletmek istemedim ve ChartItem
nesnelerimi yararsız serileştirmeye özgü niteliklerle kirletmek de istemedim .
is
üst üste korkunç yığılmış olarak artan siklomatik karmaşıklık .
magic
; sihir diye bir şey yoktur.
Statik yazılan dillerin (CLR), DLR'de (dinamik dil çalışma zamanı) çalışan dinamik dillerle (python, ruby ...) birlikte çalışmasını kolaylaştırır, bkz. MSDN :
Örneğin, C # 'da XML'de bir sayacı artırmak için aşağıdaki kodu kullanabilirsiniz.
Scriptobj.SetProperty("Count", ((int)GetProperty("Count")) + 1);
DLR kullanarak, aynı işlem için aşağıdaki kodu kullanabilirsiniz.
scriptobj.Count += 1;
MSDN şu avantajları listeler:
- Dinamik Dilleri .NET Framework'e Aktarmayı Kolaylaştırır
- Statik Yazılan Dillerdeki Dinamik Özellikleri Etkinleştirir
- DLR ve .NET Framework'ün Gelecekteki Avantajlarını Sağlar
- Kütüphane ve Nesnelerin Paylaşılmasını Sağlar
- Hızlı Dinamik Sevkıyat ve Çağırma Sağlar
Daha fazla ayrıntı için MSDN'ye bakın.
Kullanım örneği:
Commun özelliği 'CreationDate' olan birçok sınıfı tüketiyorsunuz:
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; }
}
'CreationDate' Özelliğinin değerini alan bir commun yöntemi yazarsanız, yansıma kullanmanız gerekir:
static DateTime RetrieveValueOfCreationDate(Object item)
{
return (DateTime)item.GetType().GetProperty("CreationDate").GetValue(item);
}
'Dinamik' konseptiyle kodunuz çok daha zariftir:
static DateTime RetrieveValueOfCreationDate(dynamic item)
{
return item.CreationDate;
}
Çoğunlukla RAD ve Python kurbanları tarafından kod kalitesini, IntelliSense'i yok etmek ve zaman hata tespitini derlemek için kullanılacaktır.
Çalışma zamanında değerlendirilir, böylece JavaScript'te olduğu gibi türü istediğiniz gibi değiştirebilirsiniz. Bu yasal:
dynamic i = 12;
i = "text";
Ve böylece türü istediğiniz gibi değiştirebilirsiniz. Son çare olarak kullanın; faydalıdır, ancak üretilen IL açısından sahnelerin altında çok şey olduğunu duydum ve bu bir performans fiyatına gelebilir.
Benim için 'dinamik' tip değişkenleri en iyi kullanım örneği, son zamanlarda, ADO.NET ( SQLDataReader kullanarak ) bir veri erişim katmanı yazarken ve kod zaten yazılı eski saklı yordamları çağırıyordu oldu. İş mantığının büyük bölümünü içeren yüzlerce eski saklı yordam vardır. Veri erişim katmanımın, bazı manipülasyonlar yapmak için ( neredeyse hiçbiri olmasa da ) C # tabanlı iş mantığı katmanına bir tür yapılandırılmış veri döndürmesi gerekiyordu . Saklanan her yordam farklı veri kümeleri ( tablo sütunları ) döndürür . Bu nedenle, döndürülen verileri tutmak ve BLL'ye aktarmak için düzinelerce sınıf veya yapı oluşturmak yerine, oldukça zarif ve düzgün görünen aşağıdaki kodu yazdım.
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
Üzerine sayısal işleçler uygularken jenerikleri yayınlayabilirsiniz . Bu tip güvenliği sağlar ve jeneriklerin sınırlarını önler. Bu özünde * ördek yazarak:T y = x * (dynamic)x
, nerede typeof(x) is T
dynamic
Yazma için bir başka kullanım örneği , kovaryans veya kontravaryans ile ilgili bir sorun yaşayan sanal yöntemler içindir. Böyle bir örnek Clone
, çağrıldığı nesneyle aynı türdeki bir nesneyi döndüren rezil yöntemdir. Bu sorun dinamik bir dönüşle tamamen çözülmez, çünkü statik tip kontrolünü atlar, ancak en azından düz kullanırken her zaman çirkin dökümler kullanmanıza gerek yoktur object
. Aksi takdirde, atmalar örtülü olur.
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
}
}