एक त्वरित प्रणाली पास करें। सामान्य वर्ग के लिए टाइप पैरामीटर के रूप में टाइप करें


183

शीर्षक एक प्रकार का अस्पष्ट है। क्या मैं जानना चाहता हूं कि क्या यह संभव है:

string typeName = <read type name from somwhere>;
Type myType = Type.GetType(typeName);

MyGenericClass<myType> myGenericClass = new MyGenericClass<myType>();

जाहिर है, MyGenericClass के रूप में वर्णित है:

public class MyGenericClass<T>

अभी, कंपाइलर की शिकायत है कि 'टाइप या नेमस्पेस' माय टाइप 'नहीं मिल सका। "ऐसा करने का एक तरीका मिल गया है।


जेनरिक! = टेम्प्लेट। सभी सामान्य प्रकार के चरों का संकलन समय पर किया जाता है न कि रनटाइम पर। यह उन स्थितियों में से एक है जहां 4.0 का 'गतिशील' प्रकार उपयोगी हो सकता है।

1
@Ill - किस तरह से? जब जेनरिक के साथ प्रयोग किया जाता है, तो वर्तमान CTP के तहत आप अनिवार्य रूप से <ऑब्जेक्ट> संस्करण (जब तक मैं एक चाल याद नहीं कर रहा हूं) को कॉल कर रहा हूं
मार्क ग्रैवल

@MarcGravell आप foo.Method((dynamic)myGenericClass)रन टाइम विधि बाइंडिंग के लिए उपयोग कर सकते हैं , प्रभावी रूप से एक प्रकार की विधि ओवरलोड के लिए सेवा लोकेटर पैटर्न।
क्रिस मैरिसिक

@ChrisMarisic हाँ, कुछ सामान्य के लिए public void Method<T>(T obj)- एक चाल मैं और अधिक है कि टिप्पणी के बाद से पिछले 6 वर्षों में कई बार की तुलना में उपयोग किया है, पी
मार्क Gravell

@MarcGravell क्या इसमें संशोधन करने का कोई तरीका है ताकि यह विधि इसे लागू कर सके?
बार्लोप

जवाबों:


220

आप प्रतिबिंब के बिना ऐसा नहीं कर सकते। हालांकि, आप इसे प्रतिबिंब के साथ कर सकते हैं। यहाँ एक पूर्ण उदाहरण है:

using System;
using System.Reflection;

public class Generic<T>
{
    public Generic()
    {
        Console.WriteLine("T={0}", typeof(T));
    }
}

class Test
{
    static void Main()
    {
        string typeName = "System.String";
        Type typeArgument = Type.GetType(typeName);

        Type genericClass = typeof(Generic<>);
        // MakeGenericType is badly named
        Type constructedClass = genericClass.MakeGenericType(typeArgument);

        object created = Activator.CreateInstance(constructedClass);
    }
}

नोट: यदि आपका सामान्य वर्ग कई प्रकार स्वीकार करता है, तो उदाहरण के लिए, जब आप टाइप नाम छोड़ते हैं, तो आपको कॉमा को शामिल करना होगा:

Type genericClass = typeof(IReadOnlyDictionary<,>);
Type constructedClass = genericClass.MakeGenericType(typeArgument1, typeArgument2);

1
ठीक है, यह अच्छा है, लेकिन किसी ने कॉल करने के तरीकों के बारे में कैसे जाना है? अधिक प्रतिबिंब?
राबर्ट सी। बर्थ

7
ठीक है, अगर आप अपने जेनेरिक प्रकार को गैर-जेनेरिक इंटरफ़ेस लागू करने के साथ दूर कर सकते हैं, तो आप उस इंटरफ़ेस को डाल सकते हैं। वैकल्पिक रूप से, आप अपने खुद के सामान्य विधि है जो काम आप सामान्य साथ क्या करना चाहते के सभी करता है लिखते हैं, और कह सकते हैं कि प्रतिबिंब के साथ।
जॉन स्कीट

1
हाँ, मैं यह नहीं जानता कि कैसे बनाया गया उपयोग करने के लिए यदि आपके पास लौटाए गए प्रकार के बारे में एकमात्र जानकारी प्रकार प्रकार में परिवर्तनशील है? मुझे ऐसा लगता है कि आपको उस वैरिएबल को कास्ट करना होगा, लेकिन आप नहीं जानते कि यह क्या है इसलिए मुझे यकीन नहीं है कि आप प्रतिबिंब के साथ ऐसा कर सकते हैं। एक अन्य प्रश्न यदि वस्तु उदाहरण के उदाहरण के लिए है, यदि आप इसे वस्तु चर के रूप में पास करते हैं तो उदाहरण के लिए सूची <int> इस काम को मिटा दें? क्या बनाए गए चर को इंट के रूप में माना जाएगा?
theringostarrs

6
@ RobertC.Barth आप "ऑब्जेक्ट" के बजाय उदाहरण प्रकार "गतिशील" में "बनाई गई" ऑब्जेक्ट भी बना सकते हैं। इस तरह से आप इस पर तरीकों को कॉल कर सकते हैं, और मूल्यांकन को रनटाइम तक स्थगित कर दिया जाएगा।
मैकगर्ल

4
@balanza: आप MakeGenericMethod का उपयोग करते हैं।
जॉन स्कीट

14

दुर्भाग्य से नहीं है। सामान्य तर्कों को कंपाइल समय में 1) एक मान्य प्रकार या 2) अन्य जेनेरिक पैरामीटर के रूप में फिर से लागू किया जाना चाहिए। प्रतिबिंब का उपयोग करने के बड़े हथौड़ा के बिना रनटाइम मूल्यों के आधार पर सामान्य उदाहरण बनाने का कोई तरीका नहीं है।


2

कुछ अतिरिक्त कैसे कैंची कोड के साथ चलाने के लिए। मान लीजिए कि आपके पास एक वर्ग है

public class Encoder() {
public void Markdown(IEnumerable<FooContent> contents) { do magic }
public void Markdown(IEnumerable<BarContent> contents) { do magic2 }
}

मान लीजिए कि रनटाइम में आपके पास एक FooContent है

यदि आप संकलन समय पर बाँधना चाहते थे तो आप चाहेंगे

var fooContents = new List<FooContent>(fooContent)
new Encoder().Markdown(fooContents)

हालाँकि आप इसे रनटाइम पर नहीं कर सकते। रनटाइम पर ऐसा करने के लिए आप निम्न कार्य करेंगे:

var listType = typeof(List<>).MakeGenericType(myType);
var dynamicList = Activator.CreateInstance(listType);
((IList)dynamicList).Add(fooContent);

गतिशील रूप से आह्वान करना Markdown(IEnumerable<FooContent> contents)

new Encoder().Markdown( (dynamic) dynamicList)

dynamicविधि कॉल के उपयोग पर ध्यान दें । रनटाइम पर dynamicListहो जाएगा List<FooContent>(अतिरिक्त भी किया जा रहा है IEnumerable<FooContent>) के बाद से गतिशील का भी उपयोग अभी भी एक जोरदार टाइप किया भाषा रन टाइम बांधने की मशीन उचित चयन करेंगे करने के लिए निहित है Markdownविधि। यदि कोई सटीक प्रकार मिलान नहीं है, तो यह ऑब्जेक्ट पैरामीटर विधि की तलाश करेगा और यदि न तो रनटाइम बाइंडर अपवाद को मैच किया जाएगा, तो यह चेतावनी दी जाएगी कि कोई भी विधि मेल नहीं खाती है।

इस दृष्टिकोण के लिए स्पष्ट ड्रा संकलन समय पर प्रकार की सुरक्षा का एक बड़ा नुकसान है। फिर भी इन पंक्तियों के साथ कोड आपको बहुत गतिशील अर्थों में काम करने देगा कि रनटाइम अभी भी पूरी तरह से टाइप किया गया है जैसा कि आप इसे होने की उम्मीद करते हैं।


2

मेरी आवश्यकताएं थोड़ी अलग थीं, लेकिन उम्मीद है कि किसी की मदद करूंगा। मुझे किसी कॉन्फिग से टाइप पढ़ने और जेनेरिक टाइप को डायनामिकली टाइप करने की जरूरत थी।

namespace GenericTest
{
    public class Item
    {
    }
}

namespace GenericTest
{
    public class GenericClass<T>
    {
    }
}

अंत में, यहाँ है कि आप इसे कैसे कहते हैं। बैकटिक के साथ प्रकार को परिभाषित करें

var t = Type.GetType("GenericTest.GenericClass`1[[GenericTest.Item, GenericTest]], GenericTest");
var a = Activator.CreateInstance(t);

0

यदि आप जानते हैं कि किस प्रकार पारित किया जाएगा तो आप बिना प्रतिबिंब के ऐसा कर सकते हैं। एक स्विच स्टेटमेंट काम करेगा। जाहिर है, यह केवल सीमित संख्या में मामलों में काम करेगा, लेकिन यह प्रतिबिंब की तुलना में बहुत तेज होगा।

public class Type1 { }

public class Type2 { }

public class Generic<T> { }

public class Program
{
    public static void Main()
    {
        var typeName = nameof(Type1);

        switch (typeName)
        {
            case nameof(Type1):
                var type1 = new Generic<Type1>();
                // do something
                break;
            case nameof(Type2):
                var type2 = new Generic<Type2>();
                // do something
                break;
        }
    }
}

एक बार जब आप 100 वर्ग के साथ काम करना शुरू कर देते हैं तो यह बदसूरत हो जाता है।
माइकल जी

0

इस स्निपेट में मैं यह दिखाना चाहता हूं कि गतिशील रूप से बनाई गई सूची कैसे बनाई जाए और उसका उपयोग कैसे किया जाए। उदाहरण के लिए, मैं यहाँ गतिशील सूची में जोड़ रहा हूँ।

void AddValue<T>(object targetList, T valueToAdd)
{
    var addMethod = targetList.GetType().GetMethod("Add");
    addMethod.Invoke(targetList, new[] { valueToAdd } as object[]);
}

var listType = typeof(List<>).MakeGenericType(new[] { dynamicType }); // dynamicType is the type you want
var list = Activator.CreateInstance(listType);

AddValue(list, 5);

इसी तरह आप सूची में किसी अन्य विधि को लागू कर सकते हैं।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.