How do I detect that an object is a generic collection, and what types it contains?

后端 未结 3 447
孤城傲影
孤城傲影 2020-12-10 04:58

I have a string serialization utility that takes a variable of (almost) any type and converts it into a string. Thus, for example, according to my convention, an integer va

相关标签:
3条回答
  • 2020-12-10 05:28

    At the most basic level, all generic lists implement IEnumerable<T>, which is in itself a descendant of IEnumerable. If you want to serialize a list, then you could just cast it down to IEnumerable and enumerate the generic objects inside them.

    The reason why you can't do

    Type lt = t.GetGenericArguments()[0];
    List<lt> x = (List<lt>)o;
    stringifyList(x);
    

    is because generics still need to be statically strong typed, and what you're trying to do is to create a dynamic type. List<string> and List<int>, despite using the same generic interface, are two completely distinct types, and you can't cast between them.

    List<int> intList = new List<int>();
    List<string> strList = intList; // error!
    

    What type would stringifyList(x) receive? The most basic interface you could pass here is IEnumerable, since IList<T> doesn't inherit from IList.

    To serialize the generic list, you need to keep information on the original Type of the list so that you can re-create with Activator. If you want to optimize slightly so that you don't have to check the type of each list member in your stringify method, you could pass the Type you've extracted from the list directly.

    0 讨论(0)
  • 2020-12-10 05:38

    Re your conundrum; I'm assuming stringifyList is a generic method? You would need to invoke it with reflection:

    MethodInfo method = typeof(SomeType).GetMethod("stringifyList")
                .MakeGenericMethod(lt).Invoke({target}, new object[] {o});
    

    where {target} is null for a static method, or this for an instance method on the current instance.

    Further - I wouldn't assume that all collections are a: based on List<T>, b: generic types. The important thing is: do they implement IList<T> for some T?

    Here's a complete example:

    using System;
    using System.Collections.Generic;
    static class Program {
        static Type GetListType(Type type) {
            foreach (Type intType in type.GetInterfaces()) {
                if (intType.IsGenericType
                    && intType.GetGenericTypeDefinition() == typeof(IList<>)) {
                    return intType.GetGenericArguments()[0];
                }
            }
            return null;
        }
        static void Main() {
            object o = new List<int> { 1, 2, 3, 4, 5 };
            Type t = o.GetType();
            Type lt = GetListType(t);
            if (lt != null) {
                typeof(Program).GetMethod("StringifyList")
                    .MakeGenericMethod(lt).Invoke(null,
                    new object[] { o });
            }
        }
        public static void StringifyList<T>(IList<T> list) {
            Console.WriteLine("Working with " + typeof(T).Name);
        }
    }
    
    0 讨论(0)
  • 2020-12-10 05:48

    Use the Type to gather the required information.

    For generic objects, call GetType() to get their type and then check IsGenericType to find out if it is generic at all. If it is, you can get the generic type definition, which can be compared for instance like this: typeof(List<>)==yourType.GetGenericTypeDefinition(). To find out what the generic types are, use the method GetGenericArguments, which will return an array of the types used.

    To compare types, you can do the following: if (typeof(int).IsAssignableFrom(yourGenericTypeArgument)).


    EDIT to answer followup:

    Just make your stringifyList method accept an IEnumerable (not generic) as parameter and maybe also the known generic type argument, and you'll be fine; you can then use foreach to go over all items and handle them depending on the type argument if necessary.

    0 讨论(0)
提交回复
热议问题