I finally figured it out. This is the decision itself. To explain in detail, I have to introduce some non-abstract coding:
This is about a value converter. My goal is simple so that users add their own value converters . At the conversion stage, I will first check the built-in type converters (as IConvertible ), I will look for the current IConvertible assembly for all classes of the user envelope that inherit the specific abstract class provided by me. And interface is implemented by this abstract class to make a constraint for later reflection. Then I filter those reflected classes that match.
Here is the base class and interface (all nested):
private interface ICustomConverter { Type SourceType { get; } object CallConvert(string input); } public abstract class CustomConverter<T> : ICustomConverter { public abstract T Convert(string input); public Type SourceType { get { return typeof (T); } } object ICustomConverter.CallConvert(string input) { return Convert(input); } }
I made the interface private in the parent class and implemented it explicitly. That the CallConvert() method is not called out.
the general parameter T is the type to convert the string value to. eg.
public class Int32Converter:CustomConverter<int> { }
This is easy to handle because the type of conversion goal is not common. all i have to do is get all the types that implement ICustomConverter and make a generic type from CustomConverter<T> with the given int , thus CustomConverter<int> . Then I filter these classes for what comes from CustomConverter<int> , and here I found Int32Converter .
Later I came across this situation:
public class ListConverter<T>:CustomConverter<List<T>> { }
and
public class DictConverter<T,U>:CustomConverter<Dictionary<T,U>> { }
I used a similar process to deal with them. But after I made the general type CustomConverter<List<T>> , I found that ListConverter<T> cannot be obtained from CustomConverter<List<T>> and CustomConverter<List<T>> cannot be assigned to ListConverter<T> (which I checked with IsAssignableFrom() and IsSubclassOf() ).
I guess the reason is that a generic type means more than one type before assigning generic parameters. It sounds weird, but anyway. The compiler does not know that T in CustomConverter<List<T>> and ListConverter<T> means the same TYPE Actually I can write it as CustomConverter<List<T>> and ListConverter<U> , and then you tell me the relation inheritance between them.
And checking the base type will not work here, since ListConverter<T> and DictConverter<T,U> use the same root class. This means that if I search for ListConverter<T> , I will get DictConverter<T,U> also using the base class check method (hierarchy loop check). Therefore, I still have to create a common type, and then check the general arguments and compare the types.
The fact is that I need to look for a specific class whose general parameters are used as general arguments in its general generic parameter. Kind of twisted, but now itβs clear.
Here is the final Convertion solution:
public static object ToObject(Type type, string value) { if (type == null) throw new ArgumentNullException("type"); if (!typeof (IConvertible).IsAssignableFrom(type)) { if (type.IsGenericType) { Type converterType = typeof (CustomConverter<>).MakeGenericType(type); Type genericConverter = typeof (ICustomConverter).Assembly.Types(Flags.Public) .SingleOrDefault( t => typeof (ICustomConverter).IsAssignableFrom(t) && t.IsGenericType && t.GetGenericArguments().Length == type.GetGenericArguments().Length && !t.IsAbstract && t.MakeGenericType(type.GetGenericArguments()).IsSubclassOf(converterType)); if (genericConverter != null) { Type customConverter = genericConverter.MakeGenericType(type.GetGenericArguments()); object instance = customConverter.CreateInstance(); if (instance is ICustomConverter) return ((ICustomConverter) instance).CallConvert(value); } } else { Type converterType = typeof (CustomConverter<>).MakeGenericType(type); Type customConverter = typeof (ICustomConverter).Assembly.Types(Flags.Public) .SingleOrDefault(t => t.IsSubclassOf(converterType)); if (customConverter != null) { object instance = customConverter.CreateInstance(); if (instance is ICustomConverter) return ((ICustomConverter) instance).CallConvert(value); } } throw new ArgumentException("type is not IConvertible and no custom converters found", type.Name()); } TypeConverter converter = TypeDescriptor.GetConverter(type); return converter.ConvertFromString(value); }
I also checked GetGenericArguments().Length in the case of List<T> messes with Dictionary<TKey,TValue> .
Note: any custom extension method is used.