How to check if a type is a valid general argument?

The code that I have so far is as follows: I want to decide to get rid of try-catch:

public static bool IsNeverValidGenericArgument(this Type type) { var elementType=type.GetElementType(); if(null!=elementType) { if(type.IsArray) try { typeof(IList<>).MakeGenericType(elementType); return false; } catch(ArgumentException) { } catch(TypeLoadException) { } return true; // pointer or byref } return typeof(void)==type||typeof(RuntimeArgumentHandle)==type || typeof(ArgIterator)==type||typeof(TypedReference)==type; } 

I'm trying to write dynamic type building code, and my code will call GetInterfaces() for each type passed, but some of the types passed by consumer code can TypeLoadException in RuntimeType internally (e.g. typeof(ArgIterator).MakeArrayType().MakeArrayType() in 3.5, but not 4.0+), I need to check if it is never a valid general argument. trap attempts, but nothing good.

Please note that the cases that he throws may vary in different versions of the .Net framework .


Edit:

Alternative method:

 public static bool IsNeverValidGenericArgument(this Type type) { var elementType=type.GetElementType(); if(null!=elementType) { if(type.IsArray) return elementType.IsNeverValidGenericArgument(); return true; // pointer or byref } return typeof(void)==type||typeof(RuntimeArgumentHandle)==type || typeof(ArgIterator)==type||typeof(TypedReference)==type; } 

But this will report some types as invalid, which in fact would not be the cause of the exception in RuntimeType , for example typeof(ArgIterator).MakeArrayType(2).MakeArrayType() .

I know that some types are not nominated, but I cannot avoid their use in consumer code.

+7
c # clr
source share
5 answers

When you try to build a generic type with argument typeof(ArgIterator).MakeArrayType().MakeArrayType() , this is an internal native CLR that throws an exception. The most important conclusion from this fact is that it is a detail of the CLR implementation that throws, and it is not part of the standard or publicly open API that determines the validity of a general type argument. This means that there is no good way to determine if a generic type can be created without trying at all. EDIT: This also means that there is no good way to determine if something will work on a particular version of the CLR and not work with another.

But, more importantly, if you try to build a generic type with an invalid argument, this is really an exceptional case, and the right course of action is to throw an exception. I can't talk about what your code is doing, but if you are worried that your consumers are referencing it with classes that throw a TypeLoadException , you should probably let these errors bubble up so that the consumer knows that there is a problem.

TL; DR: You probably shouldn't do everything you are trying to do to deal with the exception situation. Just let it quit.

+2
source share

Is there a problem because your client potentially passes types that don't have a valid public constructor without parameters? If so, you can limit the input that they are allowed to send to you by adding a condition to your general method:

 public class MyClass<T> where T : new() { } 

This code will only allow common T types that have an open constructor without parameters. You can find more information about where the offer is here.

I don’t know how your arguments of variational type are implemented, but you could take to the collection params of type T with the sentence added above as follows:

 public class MyClass<T> where T : new() { public void MyMethod(params T[] items) { //...Do stuff... } } 

This will allow them to pass as many elements as they want, but limit them to the universal types that you want to support.

+1
source share

It would be helpful if you could add some test cases so that we programmers can know exactly what you expect. But you offered generosity, so I see if this is getting closer to what you want.

code results below:

A test has the definition of Test <>
The actual type is UserQuery + Test`1 [System.Int32]

 public static class Extensions { /// <summary> /// Checks whether this type has the specified definition in its ancestry. /// </summary> public static bool HasGenericDefinition(this Type type, Type definition) { return GetTypeWithGenericDefinition(type, definition) != null; } /// <summary> /// Returns the actual type implementing the specified definition from the /// ancestry of the type, if available. Else, null. /// </summary> public static Type GetTypeWithGenericDefinition(this Type type, Type definition) { if (type == null) throw new ArgumentNullException("type"); if (definition == null) throw new ArgumentNullException("definition"); if (!definition.IsGenericTypeDefinition) throw new ArgumentException("The definition needs to be a GenericTypeDefinition", "definition"); if (definition.IsInterface) foreach (var interfaceType in type.GetInterfaces()) if (interfaceType.IsGenericType && interfaceType.GetGenericTypeDefinition() == definition) return interfaceType; for (Type t = type; t != null; t = t.BaseType) if (t.IsGenericType && t.GetGenericTypeDefinition() == definition) return t; return null; } } void Main() { Type a = typeof(Test<int>); Type b = typeof(Test<>); if(a.HasGenericDefinition(b)) Console.WriteLine("Test<int> has definition Test<>"); Type c = a.GetTypeWithGenericDefinition(b); Console.WriteLine("Actual Type is {0}", c.ToString()); } public class Test<T> { public Test() { } } 
+1
source share

I do not see that this can be done much better than what you already have. Here is my version:

 // Predicts whether the given type cannot be used as a type argument. public static bool IsNeverValidGenericArgument(this Type type) { if (type == null) throw new ArgumentNullException("type"); // Pointer types and ByRef types. if (type.IsPointer || type.IsByRef) return true; // The following four special cases were found by reflecting through all types in mscorlib.dll, System.dll, and System.Core.dll. // The list may be different in other versions of the framework. var exceptions = new HashSet<Type> { typeof(ArgIterator), typeof(RuntimeArgumentHandle), typeof(TypedReference), typeof(void), }; return exceptions.Contains(type); } 

Note that this only takes into account if the type system builds a closed typical type with type as an argument to its type. There are types that are completely meaningless, for example:

 typeof(IList<>).MakeGenericType(typeof(Math)) // will work // but C# does not allow the notation IList<Math> 

where System.Math is a static class (abstract and private). My method will still return false for static classes.

Some types do not even exist, for example type = typeof(int).MakeByRefType().MakeArrayType() (will type = typeof(int).MakeByRefType().MakeArrayType() ), so these bastards cannot be checked by my method.

+1
source share

The following is the version of your "alternative" version of the code that fixes the problem with ArgIterator.

  public static bool IsNeverValidGenericArgument(this Type type) { return type.IsNeverValidGenericArgument(true); } private static bool IsNeverValidGenericArgument(this Type type, bool isRoot) { var elementType = type.GetElementType(); if (null != elementType) { if (type.IsArray) return elementType.IsNeverValidGenericArgument(false); return true; // pointer or byref } if (isRoot) { return typeof(void) == type || typeof(RuntimeArgumentHandle) == type || typeof(ArgIterator) == type || typeof(TypedReference) == type; } else { return (typeof(void) == type || typeof(TypedReference) == type); } } 
0
source share

All Articles