केवल रनटाइम पर ज्ञात एक प्रकार के पैरामीटर के साथ एक सामान्य विधि को कॉल dynamic
करना प्रतिबिंब एपीआई के बजाय एक प्रकार का उपयोग करके बहुत सरल किया जा सकता है।
इस तकनीक का उपयोग करने के लिए प्रकार को वास्तविक वस्तु (केवल Type
कक्षा का एक उदाहरण नहीं ) से जाना जाना चाहिए । अन्यथा, आपको उस प्रकार का ऑब्जेक्ट बनाना होगा या मानक प्रतिबिंब एपीआई समाधान का उपयोग करना होगा । आप Activator.CreateInstance पद्धति का उपयोग करके ऑब्जेक्ट बना सकते हैं ।
यदि आप एक सामान्य विधि को कॉल करना चाहते हैं, कि "सामान्य" उपयोग में इसके प्रकार का अनुमान लगाया गया होगा, तो यह केवल अज्ञात प्रकार के ऑब्जेक्ट को कास्टिंग करने के लिए आता है dynamic
। यहाँ एक उदाहरण है:
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)"
}
}
}
और यहाँ इस कार्यक्रम का उत्पादन है:
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
Process
एक जेनेरिक इंस्टेंस विधि है जो पास किए गए तर्क के वास्तविक प्रकार ( GetType()
विधि का उपयोग करके ) और सामान्य पैरामीटर के प्रकार ( typeof
ऑपरेटर का उपयोग करके ) लिखता है ।
ऑब्जेक्ट तर्क को dynamic
टाइप करने के लिए हमने रनटाइम तक टाइप पैरामीटर प्रदान करने को स्थगित कर दिया। जब Process
विधि को dynamic
तर्क के साथ बुलाया जाता है तो संकलक इस तर्क के प्रकार के बारे में परवाह नहीं करता है। संकलक कोड उत्पन्न करता है कि रनटाइम में वास्तविक प्रकार के पारित तर्कों (प्रतिबिंब का उपयोग करके) की जांच करता है और कॉल करने के लिए सबसे अच्छा तरीका चुनता है। यहाँ केवल यह एक सामान्य विधि है, इसलिए इसे उचित प्रकार के पैरामीटर के साथ लागू किया जाता है।
इस उदाहरण में, आउटपुट वैसा ही है जैसा आपने लिखा है:
foreach (var o in objects)
{
MethodInfo method = typeof(Service).GetMethod("Process");
MethodInfo generic = method.MakeGenericMethod(o.GetType());
generic.Invoke(service, new object[] { o });
}
गतिशील प्रकार वाला संस्करण निश्चित रूप से छोटा और लिखने में आसान है। आपको कई बार इस फ़ंक्शन को कॉल करने के प्रदर्शन के बारे में चिंता नहीं करनी चाहिए। उसी प्रकार के तर्कों के साथ अगली कॉल डीएलआर में कैशिंग तंत्र के लिए तेजी से धन्यवाद होनी चाहिए । बेशक, आप उस कोड को लिख सकते हैं जो कैश ने प्रतिनिधियों को आमंत्रित किया है, लेकिन जिस dynamic
प्रकार का उपयोग करके आपको यह व्यवहार मुफ्त में मिलता है।
यदि आप जिस जेनेरिक विधि को कॉल करना चाहते हैं, उसमें पैरामीरिज्ड प्रकार का तर्क नहीं है (इसलिए इसके प्रकार के पैरामीटर का अनुमान नहीं लगाया जा सकता है) तो आप निम्न उदाहरण में जेनेरिक पद्धति के आह्वान को सहायक विधि से लपेट सकते हैं:
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) + ">");
}
}
बढ़ी हुई प्रकार की सुरक्षा
dynamic
प्रतिबिंब एपीआई का उपयोग करने के प्रतिस्थापन के रूप में ऑब्जेक्ट का उपयोग करने के बारे में वास्तव में बहुत अच्छा है कि आप केवल इस विशेष प्रकार की संकलन समय की जांच खो देते हैं जिसे आप रनटाइम तक नहीं जानते हैं। अन्य तर्क और विधि का नाम हमेशा की तरह संकलक द्वारा स्थिर रूप से विश्लेषण किया जाता है। यदि आप अधिक तर्कों को हटाते हैं या जोड़ते हैं, तो उनके प्रकार या नाम विधि का नाम बदलें, फिर आपको एक संकलन-समय त्रुटि मिलेगी। यह तब नहीं होगा जब आप विधि नाम को स्ट्रिंग के रूप में प्रदान करते हैं Type.GetMethod
और ऑब्जेक्ट में सरणी के रूप में तर्क देते हैं MethodInfo.Invoke
।
नीचे एक सरल उदाहरण दिया गया है, जो दिखाता है कि संकलन समय (टिप्पणी कोड) और रनटाइम के दौरान कुछ त्रुटियों को कैसे पकड़ा जा सकता है। यह यह भी दिखाता है कि DLR किस विधि को कॉल करने की कोशिश करता है।
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);
}
}
यहाँ हम फिर से dynamic
टाइप का तर्क देकर कुछ विधि निष्पादित करते हैं । केवल पहले तर्क के प्रकार का सत्यापन रनटाइम के लिए स्थगित कर दिया गया है। यदि आप जिस विधि को कॉल कर रहे हैं उसका नाम मौजूद नहीं है या यदि अन्य तर्क अमान्य हैं (तर्कों की गलत संख्या या गलत प्रकार) तो आपको संकलक त्रुटि मिलेगी।
जब आप dynamic
किसी विधि पर तर्क पास करते हैं तो यह कॉल हाल ही में बाध्य होती है । विधि अधिभार संकल्प रनटाइम पर होता है और सबसे अच्छा अधिभार चुनने की कोशिश करता है। इसलिए यदि आप ProcessItem
विधि को ऑब्जेक्ट के साथ BarItem
टाइप करते हैं तो आप वास्तव में गैर-जेनेरिक विधि को कॉल करेंगे, क्योंकि यह इस प्रकार के लिए एक बेहतर मेल है। हालाँकि, जब आप Alpha
टाइप का एक तर्क पास करते हैं तो आपको एक रनटाइम त्रुटि मिलेगी क्योंकि कोई भी तरीका नहीं है जो इस ऑब्जेक्ट को संभाल सकता है (एक सामान्य विधि में बाधा है where T : IItem
और Alpha
क्लास इस इंटरफ़ेस को लागू नहीं करता है)। लेकिन यह पूरी बात है। कंपाइलर के पास यह जानकारी नहीं है कि यह कॉल वैध है। आप एक प्रोग्रामर के रूप में यह जानते हैं, और आपको यह सुनिश्चित करना चाहिए कि यह कोड बिना त्रुटियों के चलता है।
रिटर्न प्रकार गोच
आप गतिशील प्रकार का एक पैरामीटर के साथ एक गैर शून्य विधि बुला रहे हैं, अपनी वापसी प्रकार शायद होगा होना dynamic
भी । इसलिए यदि आप पिछले उदाहरण को इस कोड में बदलेंगे:
var result = ProcessItem((dynamic)testObjects[i], "test" + i, i);
तब परिणाम वस्तु का प्रकार होगा dynamic
। ऐसा इसलिए है क्योंकि कंपाइलर को हमेशा नहीं पता होता है कि किस विधि को बुलाया जाएगा। यदि आप फ़ंक्शन कॉल का रिटर्न प्रकार जानते हैं, तो आपको इसे आवश्यक प्रकार में बदलना चाहिए ताकि बाकी कोड सांख्यिकीय रूप से टाइप हो जाए:
string result = ProcessItem((dynamic)testObjects[i], "test" + i, i);
यदि प्रकार मेल नहीं खाता, तो आपको रनटाइम त्रुटि मिलेगी।
दरअसल, यदि आप पिछले उदाहरण में परिणाम मान प्राप्त करने का प्रयास करते हैं तो आपको दूसरे लूप पुनरावृत्ति में रनटाइम त्रुटि मिलेगी। ऐसा इसलिए है क्योंकि आपने एक शून्य फ़ंक्शन के रिटर्न मान को बचाने की कोशिश की है।