Generic Type Inheritance

public class BaseGenericType<T> { } public class SubGenericType<T>: BaseGenericType<List<T>> { } 

I have two common types above that inherit from another, but are still common. The strange thing I cannot understand is that typeof(SubGenericType<>).IsSubclassOf(typeof(BaseGenericType<>)) returns false. And typeof(SubGenericType<>).IsSubclassOf(typeof(BaseGenericType<List<>>)) still returns false. I tried GetGenericTypeDefinition() and MakeGenericType() and GetGenericArguments() to check inheritance while still not working. But typeof(SubGenericType<int>).IsSubclassOf(typeof(BaseGenericType<List<int>>)) returns true.

What I want is to get all classes by reflection, and then capture a specific class that inherits from the generic type passed to.

eg.

(1) List<int> β†’

(2) get a definition of a general type ==> List<T> β†’

(3) do a generic ==> BaseGenericType<List<T>> β†’

(4) find the subclass ==> SubGenericType<T>

(5) make generic ==> SubGenericType<int>

Nothing was found in step (4), although I actually have a SubGenericType<T> . Why is this?

+5
source share
2 answers

Once I wrote this method to check the inheritance of a type of a generic type:

  static bool IsSubclassOfOpenGeneric(Type generic, Type toCheck) { while (toCheck != null && toCheck != typeof(object)) { var cur = toCheck.IsGenericType ? toCheck.GetGenericTypeDefinition() : toCheck; if (generic == cur) { return true; } toCheck = toCheck.BaseType; } return false; } 

This returns true:

 IsSubclassOfOpenGeneric(typeof(BaseGenericType<>), typeof(SubGenericType<int>)) 

It does not check interfaces though.

By the way, usually, if you have such a relationship, and you yourself write all the classes, consider using interfaces . It is much easier to deal with. For example, you can have an IGenericType interface without common arguments. You don't seem to care about the generic type and just want access to members that are independent of the generic type. Sometimes you just want to check if this is one of them. And you can use type dispersion.

+3
source
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.

+1
source

All Articles