How to determine that an object is a general collection and what types it contains?

I have a string serialization utility that takes a variable of (almost) any type and converts it to a string. Thus, for example, according to my agreement, the integer value 123 will be serialized as "i: 3: 123" (i = integer; 3 = string length; 123 = value).

The utility processes all primitive types, as well as some non-general collections, such as ArrayLists and Hashtables. The interface has the form

public static string StringSerialize(object o) {}

and internally I find out what type of object and serializes it accordingly.

Now I want to update my utility for handling shared collections. The funny thing is that I can’t find a suitable function to detect that the object is a common collection, and what types it contains are both pieces of information that I need to serialize correctly. Today I use form coding

if (o is int) {// do something}

but that doesn't seem to work with generics.

What do you recommend?




EDIT: thanks Lucero , I came closer to the answer, but I was stuck in this little syntax puzzle:

 if (t.IsGenericType) { if (typeof(List<>) == t.GetGenericTypeDefinition()) { Type lt = t.GetGenericArguments()[0]; List<lt> x = (List<lt>)o; stringifyList(x); } } 

This code does not compile because " lt " is not resolved as the <T> argument of the List<> object. Why not? And what is the correct syntax?

+9
generics c # typechecking
Apr 16 '09 at 8:20
source share
3 answers

Repeat your riddle; I assume stringifyList is a generic method? You will need to call it with reflection:

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

where {target} is null for the static method or this for the instance method in the current instance.

Further - I would not assume that all collections are: based on List<T> , b: generic types. Important: do they implement IList<T> for some T ?

Here is 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); } } 
+5
Apr 16 '09 at 9:07
source share
β€” -

Use the type to collect the required information.

For generic objects, call GetType () to get their type, and then check IsGenericType to see if it is at all. If so, you can get a generic type definition that can be compared, for example, as follows: typeof(List<>)==yourType.GetGenericTypeDefinition() . To find out what common types are, use the GetGenericArguments method, 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 the following:

Just try to accept the stringifyList IEnumerable (not generic) method as a parameter and perhaps also a well-known argument of type generic, and everything will be fine; you can use foreach to iterate over all elements and process them depending on the type argument, if necessary.

+7
Apr 16 '09 at 8:27
source share

At the most basic level, all shared lists implement IEnumerable<T> , which itself is a descendant of IEnumerable . If you want to serialize a list, you can simply pass it to IEnumerable and list the common objects inside them.

The reason you cannot do

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

that generators still have to be statically strong, and what you are trying to do is create a dynamic type. List<string> and List<int> , despite using the same common interface, are two completely different types, and you cannot use between them.

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

What type will get stringifyList(x) ? The simplest interface you could pass here is IEnumerable , since IList<T> does not inherit from IList .

To serialize a general list, you need to save information about the original type of the list so that you can recreate using Activator . If you want to optimize a bit, so that you do not need to check the type of each member of the list in the stringify method, you can pass the Type that you extracted from the list directly.

+1
Apr 16 '09 at 10:33
source share



All Articles