जेनेरिक वर्ग या विधि के सदस्य से टी का प्रकार कैसे प्राप्त करें?


674

मान लीजिए कि मेरे पास एक वर्ग या विधि में एक सामान्य सदस्य है, इसलिए:

public class Foo<T>
{
    public List<T> Bar { get; set; }

    public void Baz()
    {
        // get type of T
    }   
}

जब मैं क्लास को इंस्टेंट कर देता हूं, Tबन जाता है MyTypeObject1, तो क्लास में जेनेरिक लिस्ट प्रॉपर्टी होती है List<MyTypeObject1>:। एक गैर-जेनेरिक वर्ग में एक सामान्य विधि पर लागू होता है:

public class Foo
{
    public void Bar<T>()
    {
        var baz = new List<T>();

        // get type of T
    }
}

मैं यह जानना चाहूंगा कि मेरी कक्षा की सूची में किस प्रकार की वस्तुएँ हैं। तो सूची संपत्ति जिसे Barस्थानीय चर कहा जाता है baz, किस प्रकार का है T?

मैं नहीं कर सकता Bar[0].GetType(), क्योंकि सूची में शून्य तत्व हो सकते हैं। मैं यह कैसे कर सकता हूं?

जवाबों:


693

अगर मैं सही ढंग से समझूं, तो आपकी सूची में कंटेनर क्लास के समान ही पैरामीटर है। यदि यह मामला है, तो:

Type typeParameterType = typeof(T);

यदि आप objectएक प्रकार के पैरामीटर के रूप में होने की भाग्यशाली स्थिति में हैं, तो मार्क का उत्तर देखें


4
लोल - हाँ, बहुत सही; मैं मान लिया है कि ओपी केवल था object, IList, या इसी तरह - लेकिन यह बहुत ही अच्छी तरह से सही जवाब हो सकता है।
मार्क Gravell

32
मुझे प्यार है कि कैसे पठनीय typeofहै। यदि आप टी का प्रकार जानना चाहते हैं, तो बस typeof(T):)
demoncodemonkey

2
मैंने वास्तव में सिर्फ टाइपोफ (टाइप) का उपयोग किया है और यह बहुत अच्छा काम करता है।
एंटोन

1
आप एक सामान्य पैरामीटर के साथ टाइपोफ़ () का उपयोग नहीं कर सकते, हालांकि।
रेनीवन

2
@ रेनेवन का उपयोग आप typeof()सामान्य पैरामीटर के साथ कर सकते हैं । क्या आपके पास कोई उदाहरण है जहां यह काम नहीं करेगा? या आप प्रकार के मापदंडों और संदर्भों को भ्रमित कर रहे हैं?
लुआं

520

(ध्यान दें: मैं यह सोचते हैं रहा है कि सब तुम्हें पता है है objectया IListया इसी तरह की है, और सूची रनटाइम पर किसी भी प्रकार के हो सकता है कि)

यदि आप जानते हैं कि यह एक है List<T>, तो:

Type type = abc.GetType().GetGenericArguments()[0];

सूचकांक को देखने के लिए एक और विकल्प है:

Type type = abc.GetType().GetProperty("Item").PropertyType;

नए TypeInfo का उपयोग कर:

using System.Reflection;
// ...
var type = abc.GetType().GetTypeInfo().GenericTypeArguments[0];

1
टाइप प्रकार = abc.GetType ()। GetGenericArguments () [0]; ==> सीमा से बाहर सरणी सूचकांक ...
पैट्रिक Desjardins

28
@Daok: तो यह एक सूची <टी> नहीं है
मार्क Gravell

बाइंडिंगलिस्ट या सूची या किसी भी वस्तु के लिए कुछ चाहिए जो कि <T> रखता हो। मैं एक कस्टम बाइंडिंगलिस्ट व्यू <टी> का उपयोग कर रहा हूं
पैट्रिक डेसजार्डिन्स

1
BindingList <T>, हमारे BindingListView <T> को BindingList <T> से विरासत में मिला हुआ प्रयास दें और मेरे पास आपके विकल्प के दोनों प्रयास हैं और यह काम नहीं करता है। मैं कुछ गलत कर सकता हूं ... लेकिन मुझे लगता है कि यह समाधान टाइप सूची <T> के लिए काम करता है, लेकिन अन्य प्रकार की सूची में नहीं।
पैट्रिक डेसजार्डिन्स

प्रकार टाइप करें = abc.GetType ()। GetProperty ("आइटम")। PropertyType; वापसी BindingListView <MyObject> MyObject के बजाय ...
पैट्रिक Desjardins

49

निम्नलिखित विस्तार विधि से आप बिना प्रतिबिंब के दूर हो सकते हैं:

public static Type GetListType<T>(this List<T> _)
{
    return typeof(T);
}

या अधिक सामान्य:

public static Type GetEnumeratedType<T>(this IEnumerable<T> _)
{
    return typeof(T);
}

उपयोग:

List<string>        list    = new List<string> { "a", "b", "c" };
IEnumerable<string> strings = list;
IEnumerable<object> objects = list;

Type listType    = list.GetListType();           // string
Type stringsType = strings.GetEnumeratedType();  // string
Type objectsType = objects.GetEnumeratedType();  // BEWARE: object

10
यह केवल तभी उपयोगी है जब आप पहले से ही Tसंकलन के समय को जानते हों । जिस स्थिति में, आपको वास्तव में किसी भी कोड की आवश्यकता नहीं है।
पुनरावर्ती

'डिफ़ॉल्ट (T) लौटें?'
फंतासी

1
@recursive: यदि आप किसी अनाम प्रकार की सूची के साथ काम कर रहे हैं तो यह उपयोगी है।
जेजे

मैंने आपका उत्तर पढ़ने से पहले ठीक वैसा ही किया था, लेकिन मैंने अपना फोन किया था ItemType
बजे

31

प्रयत्न

list.GetType().GetGenericArguments()

7
नई सूची <int> ()। GetType ()। GetGenericArguments () रिटर्न System.Type [1] यहां System.Int32 के साथ प्रवेश के रूप में
Rauhotz

@ रौहट्ज़ GetGenericArgumentsविधि एक ऐरे ऑब्जेक्ट को लौटाती है Type, जिसमें से आपको उस जेनेरिक प्रकार की ज़रूरत के अनुसार पोज़िशन की आवश्यकता होती है। जैसे Type<TKey, TValue>: आप की आवश्यकता होगी GetGenericArguments()[0]पाने के लिए TKeyऔर प्रकार GetGenericArguments()[1]प्राप्त करने के लिए TValueप्रकार
GoldBishop

14

मेरे लिए यही काम है। जहाँ myList कुछ अनजानी तरह की सूची है।

IEnumerable myEnum = myList as IEnumerable;
Type entryType = myEnum.AsQueryable().ElementType;

1
मुझे एक त्रुटि मिलती है कि इसके लिए एक प्रकार के तर्क की आवश्यकता होती है (जैसे <T>)
यूसुफ हम्फ्री

यूसुफ और अन्य, यह System.Collections में त्रुटि से छुटकारा पाने के लिए।
user2334883

1
मेरे लिए बस दूसरी लाइन की जरूरत है। ए Listपहले से ही लागू है IEnumerable, इसलिए कलाकारों को कुछ भी जोड़ना नहीं लगता है। लेकिन धन्यवाद, यह एक अच्छा समाधान है।
pipedreambomb

10

यदि आपको संपूर्ण प्रकार के चर की आवश्यकता नहीं है और केवल उस प्रकार की जांच करना चाहते हैं जिसे आप आसानी से एक अस्थायी चर बना सकते हैं और उपयोग ऑपरेटर है।

T checkType = default(T);

if (checkType is MyClass)
{}

9

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

public string ListType<T>(T value)
{
    var valueType = value.GetType().GenericTypeArguments[0].FullName;
    return valueType;
}

8

इस पर विचार करें: मैं इसका उपयोग 20 टाइप की गई सूची को उसी तरह से निर्यात करने के लिए करता हूं:

private void Generate<T>()
{
    T item = (T)Activator.CreateInstance(typeof(T));

    ((T)item as DemomigrItemList).Initialize();

    Type type = ((T)item as DemomigrItemList).AsEnumerable().FirstOrDefault().GetType();
    if (type == null) return;
    if (type != typeof(account)) //account is listitem in List<account>
    {
        ((T)item as DemomigrItemList).CreateCSV(type);
    }
}

1
यह काम नहीं करता है यदि T वास्तविक जोड़ी गई वस्तुओं का एक अमूर्त सुपरक्लास है। उल्लेख करने के लिए नहीं, बस के new T();रूप में एक ही बात करेंगे (T)Activator.CreateInstance(typeof(T));। यह आवश्यकता है कि आप जोड़ना where T : new()वर्ग / समारोह परिभाषा करने के लिए है, लेकिन आप अगर चाहते हैं वस्तुओं बनाने के लिए, कि वैसे भी किया जाना चाहिए।
Nyerguds

इसके अलावा, आप GetTypeएक FirstOrDefaultप्रविष्टि पर बुला रहे हैं जिसके परिणामस्वरूप एक संभावित अशक्त संदर्भ अपवाद है। यदि आप सुनिश्चित हैं कि यह कम से कम एक आइटम लौटाएगा, तो इसका उपयोग क्यों न करें First?
मैथियास लिकेगार्ड लोरेनजेन

6

मैं इस एक्सटेंशन विधि का उपयोग कुछ इसी तरह करने के लिए करता हूं:

public static string GetFriendlyTypeName(this Type t)
{
    var typeName = t.Name.StripStartingWith("`");
    var genericArgs = t.GetGenericArguments();
    if (genericArgs.Length > 0)
    {
        typeName += "<";
        foreach (var genericArg in genericArgs)
        {
            typeName += genericArg.GetFriendlyTypeName() + ", ";
        }
        typeName = typeName.TrimEnd(',', ' ') + ">";
    }
    return typeName;
}

public static string StripStartingWith(this string s, string stripAfter)
{
    if (s == null)
    {
        return null;
    }
    var indexOf = s.IndexOf(stripAfter, StringComparison.Ordinal);
    if (indexOf > -1)
    {
        return s.Substring(0, indexOf);
    }
    return s;
}

आप इसे इस तरह उपयोग करते हैं:

[TestMethod]
public void GetFriendlyTypeName_ShouldHandleReallyComplexTypes()
{
    typeof(Dictionary<string, Dictionary<string, object>>).GetFriendlyTypeName()
        .ShouldEqual("Dictionary<String, Dictionary<String, Object>>");
}

यह वह नहीं है जो आप खोज रहे हैं, लेकिन इसमें शामिल तकनीकों का प्रदर्शन करने में मददगार है।


नमस्ते, आपके उत्तर के लिए धन्यवाद, क्या आप "StripStartingWith" के लिए एक्सटेंशन जोड़ सकते हैं?
सेड्रिक अर्नोल्ड

1
@CedricArnould - जोड़ा गया।
केन स्मिथ

5

GetGenericArgument()विधि पर अपने उदाहरण के बेस प्रकार (जिसका वर्ग एक सामान्य वर्ग है स्थापित किया जाना है myClass<T>)। अन्यथा, यह एक प्रकार लौटाता है [0]

उदाहरण:

Myclass<T> instance = new Myclass<T>();
Type[] listTypes = typeof(instance).BaseType.GetGenericArguments();

5

आप किसी भी संग्रह प्रकार से "T" का प्रकार प्राप्त कर सकते हैं जो IEnumerable <T> को निम्नलिखित के साथ लागू करता है:

public static Type GetCollectionItemType(Type collectionType)
{
    var types = collectionType.GetInterfaces()
        .Where(x => x.IsGenericType 
            && x.GetGenericTypeDefinition() == typeof(IEnumerable<>))
        .ToArray();
    // Only support collections that implement IEnumerable<T> once.
    return types.Length == 1 ? types[0].GetGenericArguments()[0] : null;
}

ध्यान दें कि यह संग्रह प्रकारों का समर्थन नहीं करता है जो IEnumerable <T> को दो बार लागू करते हैं, जैसे

public class WierdCustomType : IEnumerable<int>, IEnumerable<string> { ... }

मुझे लगता है कि यदि आप इस का समर्थन करने के लिए आवश्यक प्रकार की एक सरणी वापस कर सकते हैं ...

साथ ही, यदि आप इसे बहुत अधिक कर रहे हैं (उदाहरण के लिए लूप में) तो आप प्रति संग्रह प्रकार को भी कैश कर सकते हैं।


1
public bool IsCollection<T>(T value){
  var valueType = value.GetType();
  return valueType.IsArray() || typeof(IEnumerable<object>).IsAssignableFrom(valueType) || typeof(IEnumerable<T>).IsAssignableFrom(valuetype);
}

1
यह इस प्रकार के लिस्ट-वाई प्रकार का है या नहीं, इस सवाल का समाधान करने के लिए प्रतीत होता है, लेकिन यह सवाल अधिक है कि यह कैसे निर्धारित किया जाए कि जेनेरिक टाइप पैरामीटर किस प्रकार के लिस्ट में जाना जाता है जो पहले से ही इनिशियलाइज़ हो गया था।
नाथन तुग्गी

1

3DGrabber के समाधान का उपयोग करना:

public static T GetEnumeratedType<T>(this IEnumerable<T> _)
{
    return default(T);
}

//and now 

var list = new Dictionary<string, int>();
var stronglyTypedVar = list.GetEnumeratedType();

0

यदि आप किसी संपत्ति के अंतर्निहित प्रकार को जानना चाहते हैं, तो यह प्रयास करें:

propInfo.PropertyType.UnderlyingSystemType.GenericTypeArguments[0]

0

मैंने इस तरह से इसे किया

internal static Type GetElementType(this Type type)
{
        //use type.GenericTypeArguments if exist 
        if (type.GenericTypeArguments.Any())
         return type.GenericTypeArguments.First();

         return type.GetRuntimeProperty("Item").PropertyType);
}

फिर इसे इस तरह से कॉल करें

var item = Activator.CreateInstance(iListType.GetElementType());

या

var item = Activator.CreateInstance(Bar.GetType().GetElementType());

-9

प्रकार:

type = list.AsEnumerable().SingleOrDefault().GetType();

1
अगर सूची में इसके खिलाफ कोई तत्व नहीं है, तो यह NullReferenceException को फेंक देगा।
रॉसडीडेड

1
SingleOrDefault()InvalidOperationExceptionदो या अधिक तत्वों के होने पर भी फेंकता है ।
डेवजीजर

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