Yalnızca çalışma zamanında bilinen bir tür parametresiyle genel bir yöntemin çağrılması dynamic, yansıma API'si yerine bir tür kullanılarak büyük ölçüde basitleştirilebilir .
Bu tekniği kullanmak için türün gerçek nesneden bilinmesi gerekir (yalnızca Typesınıfın bir örneği değil ). Aksi takdirde, bu tür bir nesne oluşturmanız veya standart yansıma API çözümünü kullanmanız gerekir . Activator.CreateInstance yöntemini kullanarak bir nesne oluşturabilirsiniz .
Eğer genel bir yöntem çağırmak istiyorsanız, "normal" kullanımda türünü çıkarmış olurdu, o zaman sadece bilinmeyen tip nesnesi döküm geliyor dynamic. İşte bir örnek:
class Alpha { }
class Beta { }
class Service
{
public void Process<T>(T item)
{
Console.WriteLine("item.GetType(): " + item.GetType()
+ "\ttypeof(T): " + typeof(T));
}
}
class Program
{
static void Main(string[] args)
{
var a = new Alpha();
var b = new Beta();
var service = new Service();
service.Process(a); // Same as "service.Process<Alpha>(a)"
service.Process(b); // Same as "service.Process<Beta>(b)"
var objects = new object[] { a, b };
foreach (var o in objects)
{
service.Process(o); // Same as "service.Process<object>(o)"
}
foreach (var o in objects)
{
dynamic dynObj = o;
service.Process(dynObj); // Or write "service.Process((dynamic)o)"
}
}
}
Ve işte bu programın çıktısı:
item.GetType(): Alpha typeof(T): Alpha
item.GetType(): Beta typeof(T): Beta
item.GetType(): Alpha typeof(T): System.Object
item.GetType(): Beta typeof(T): System.Object
item.GetType(): Alpha typeof(T): Alpha
item.GetType(): Beta typeof(T): Beta
Processiletilen bağımsız değişkenin gerçek türünü ( GetType()yöntemi kullanarak ) ve genel parametrenin türünü ( typeofişleç kullanarak ) yazan genel bir örnek yöntemidir .
Nesne bağımsız değişkenini dynamictür yazarak, çalışma zamanına kadar type parametresini sağlamayı erteledik. Ne zaman Processmetot ile çağrılır dynamicargüman sonra derleyici bu argüman türü hakkında umursamıyor. Derleyici, çalışma zamanında gerçekte aktarılan argüman türlerini (yansıma kullanarak) kontrol eden ve çağrılacak en iyi yöntemi seçen kod üretir. Burada sadece bir genel yöntem var, bu yüzden uygun bir tür parametresiyle çağrıldı.
Bu örnekte, çıktı sizin yazdığınız gibidir:
foreach (var o in objects)
{
MethodInfo method = typeof(Service).GetMethod("Process");
MethodInfo generic = method.MakeGenericMethod(o.GetType());
generic.Invoke(service, new object[] { o });
}
Dinamik tipte versiyon kesinlikle daha kısa ve yazımı daha kolaydır. Ayrıca, bu işlevi birden çok kez çağırmanın performansı hakkında endişelenmemelisiniz. DLR'deki önbellek mekanizması sayesinde aynı türden argümanlarla bir sonraki çağrı daha hızlı olmalıdır . Tabii ki, çağrılan delegeleri önbelleğe alan bir kod yazabilirsiniz, ancak dynamictürü kullanarak bu davranışı ücretsiz olarak alabilirsiniz.
Aramak istediğiniz genel yöntemin parametreli bir türden bağımsız değişkeni yoksa (bu nedenle tür parametresi çıkartılamaz), aşağıdaki yöntemdeki gibi bir yardımcı yöntemde genel yöntemin çağrılmasını sarabilirsiniz:
class Program
{
static void Main(string[] args)
{
object obj = new Alpha();
Helper((dynamic)obj);
}
public static void Helper<T>(T obj)
{
GenericMethod<T>();
}
public static void GenericMethod<T>()
{
Console.WriteLine("GenericMethod<" + typeof(T) + ">");
}
}
Artan tip güvenliği
dynamicNesneyi yansıma API'sini kullanmak yerine kullanmakla ilgili gerçekten harika olan şey, yalnızca çalışma zamanına kadar bilmediğiniz bu türün derleme zamanı denetimini kaybetmenizdir. Diğer argümanlar ve yöntemin adı derleyici tarafından her zamanki gibi statik olarak analiz edilir. Daha fazla bağımsız değişken kaldırır veya eklerseniz, türlerini değiştirin veya yöntem adını yeniden adlandırın, ardından derleme zamanı hatası alırsınız. Yöntem adını bir dize olarak Type.GetMethodve bağımsız değişkenleri nesneler dizisi olarak sağlarsanız bu gerçekleşmez MethodInfo.Invoke.
Aşağıda, derleme zamanında (yorumlanmış kod) ve çalışma zamanında bazı hataların nasıl yakalanabileceğini gösteren basit bir örnek verilmiştir. Ayrıca, DLR'nin hangi yöntemi çağıracağını nasıl çözmeye çalıştığını da gösterir.
interface IItem { }
class FooItem : IItem { }
class BarItem : IItem { }
class Alpha { }
class Program
{
static void Main(string[] args)
{
var objects = new object[] { new FooItem(), new BarItem(), new Alpha() };
for (int i = 0; i < objects.Length; i++)
{
ProcessItem((dynamic)objects[i], "test" + i, i);
//ProcesItm((dynamic)objects[i], "test" + i, i);
//compiler error: The name 'ProcesItm' does not
//exist in the current context
//ProcessItem((dynamic)objects[i], "test" + i);
//error: No overload for method 'ProcessItem' takes 2 arguments
}
}
static string ProcessItem<T>(T item, string text, int number)
where T : IItem
{
Console.WriteLine("Generic ProcessItem<{0}>, text {1}, number:{2}",
typeof(T), text, number);
return "OK";
}
static void ProcessItem(BarItem item, string text, int number)
{
Console.WriteLine("ProcessItem with Bar, " + text + ", " + number);
}
}
Burada yine argümanı dynamictüre çevirerek bir yöntem uygularız . Yalnızca ilk bağımsız değişkenin türünün doğrulanması çalışma zamanına ertelenir. Aradığınız yöntemin adı yoksa veya başka bağımsız değişkenler geçersizse (yanlış sayıda bağımsız değişken veya yanlış tür) bir derleyici hatası alırsınız.
Sınavı geçtikten sonra dynamicbir yönteme argüman sonra bu çağrı olduğunu son zamanlarda bağlanmış . Yöntem aşırı yük çözünürlüğü çalışma zamanında gerçekleşir ve en iyi aşırı yükü seçmeye çalışır. Bu nedenle, ProcessItemyöntemi bir tür nesneyle BarItemçağırırsanız, genel olmayan yöntemi çağıracaksınız, çünkü bu tür için daha iyi bir eşleşmedir. Ancak, Alphabu nesneyi işleyebilecek bir yöntem olmadığından türün bir argümanını ilettiğinizde bir çalışma zamanı hatası alırsınız (genel bir yöntem kısıtlamaya sahiptir where T : IItemve Alphasınıf bu arabirimi uygulamaz). Ama bütün mesele bu. Derleyicide bu çağrının geçerli olduğu bilgisi yok. Bir programcı olarak bunu biliyorsunuz ve bu kodun hatasız çalıştığından emin olmalısınız.
Dönüş türü gotcha
Dinamik türünün bir parametresi olmayan bir boşluk yöntemini çağırarak bittiğinde, döndürme türü muhtemelen olacaktır olmak dynamicçok . Dolayısıyla, önceki örneği bu kodla değiştirirseniz:
var result = ProcessItem((dynamic)testObjects[i], "test" + i, i);
sonuç nesnesinin türü olur dynamic. Bunun nedeni derleyicinin hangi yöntemin çağrıldığını her zaman bilmemesidir. İşlev çağrısının dönüş türünü biliyorsanız , kodun geri kalanı statik olarak yazılması için bunu örtük olarak gerekli türe dönüştürmelisiniz :
string result = ProcessItem((dynamic)testObjects[i], "test" + i, i);
Tür eşleşmezse bir çalışma zamanı hatası alırsınız.
Aslında, önceki örnekte sonuç değerini almaya çalışırsanız, ikinci döngü yinelemesinde bir çalışma zamanı hatası alırsınız. Bunun nedeni, bir void işlevinin dönüş değerini kaydetmeye çalışmanızdır.