Matias Cicero
Matias Cicero

Reputation: 26281

Passing type parameter to generic type with no constraints is throwing a constraint exception

Consider the following code:

IEnumerable<Type> oneParameterTypes = Assembly.GetAssembly(typeof(object))
                                      .GetTypes()
                                      .Where(t => t.IsGenericType)
                                      .Where(t => t.GetGenericArguments().Length == 1)
                                      .Where(t => t.GetGenericArguments().Single().GetGenericParameterConstraints().Length == 0);

oneParameterTypes should hold all the generic types in the System.dll assembly, that can be passed a single generic type parameter and apply no constraint to it.

Now, let's pass in a type parameter:

IEnumerable<Type> intParameterTypes = oneParameterTypes.Select(t => t.MakeGenericType(typeof(int)))
                                                       .ToList();

This should work, right? I mean, all the types in oneParameterTypes should have no type constraints, so System.Int32 should be a valid type.

Yet, the line throws the following exception:

GenericArguments[0], 'System.Int32', on 'System.RuntimeType+ListBuilder`1[T]' violates the constraint of type 'T'.

What is this ListBuilder'1 type and why is it in oneParameterTypes if it has a type constraint? Why is my Where filter not working?

Upvotes: 1

Views: 400

Answers (3)

Yuval Itzchakov
Yuval Itzchakov

Reputation: 149518

The documentation isn't really clear about what Type.GetGenericParameterConstraints actually looks for:

Use the IsClass property to determine whether a constraint is the base class constraint; if the property returns false, the constraint is an interface constraint. If a type parameter has no class constraint and no interface constraints, an empty array is returned.

You can implicitly understand that it will only check for a base class or interface constraint. To get the complete picture around which constraints exist, you'll need to have additional checks on GenericParameterAttributes. You can either use HasFlag or a bitwise mask:

IEnumerable<Type> oneParameterTypes = Assembly.GetAssembly(typeof(object))
              .GetTypes()
              .Where(t => t.IsGenericType)
              .Where(t => t.GetGenericArguments().Length == 1)
              .Where(t => t.GetGenericArguments().Single().GetGenericParameterConstraints().Length == 0 && !t.GenericParameterAttributes.HasFlag(GenericParameterAttributes.ReferenceTypeConstraint);

Upvotes: 1

Szabolcs D&#233;zsi
Szabolcs D&#233;zsi

Reputation: 8843

I think Chris is correct.

Try it like this:

IEnumerable<Type> oneParameterTypes = Assembly.GetAssembly(typeof (object))
    .GetTypes()
    .Where(t => t.IsGenericType)
    .Where(t => t.GetGenericArguments().Length == 1)
    .Where(t => t.GetGenericArguments().Single().GetGenericParameterConstraints().Length == 0 &&
                !genericArgument.GenericParameterAttributes.HasFlag(GenericParameterAttributes.ReferenceTypeConstraint);
    });

Upvotes: 2

Mallikarjuna
Mallikarjuna

Reputation: 130

typeof() or .GetType() method does not return the actual class, instead they will return the Type Object representation, which will help us in using the type name as a string.

Instead, you can use reflection classes and find the class of that type and use that as a parameter

Upvotes: 0

Related Questions