How to extract generic method constraints via reflection in C#? - c#

Given an object of type System.Reflection.MethodInfo how can I extract generic parameter constraints? Somehow I can not find reasonable information about this.

All you need to do is grab the generic method definition, and list the generic arguments on that:
method
.GetGenericMethodDefinition()
.GetGenericArguments()
.Select(i => i.GetGenericParameterConstraints())
.Dump();
However, note that this doesn't 100% correspond to C#'s generic type constrains - there's a bit of wiggle room. Still, if you only care about e.g. a base-type constraint, it will work fine :)
As an example, class isn't actually a type constraint at all, interestingly, while struct is "translated" as System.ValueType (not too surprising). new() isn't a type constraint either, so this method doesn't work to find that.
If you need to take those constraints into account as well, use the GenericParameterAttributes property in the Select. For example, struct constraint will give you NotNullableValueTypeConstraint | DefaultConstructorConstraint.

You are probably looking for Type.GetGenericParameterConstraints Method ()
Returns an array of Type objects that represent the constraints on the
current generic type parameter.
Also Type.GetGenericArguments Method ()
Returns an array of Type objects that represent the type arguments of
a closed generic type or the type parameters of a generic type
definition

Related

Why are generic type definitions in C# not equatable to those they inherit from [duplicate]

For example, if you run the following code...
Type IListType = new List<string>().GetType()
.GetInterface("IList`1")
.GetGenericTypeDefinition();
...and you watch IListType variable, you'll find that the whole Type instance has all properties available like FullName and others.
But what happens when you run the code bellow?
Type IListType2 = typeof(List<>).GetInterface("IList`1")
Now IListType got from a generic type definition isn't the same as the first code sample: most Type properties will return null.
The main issue with this is that IListType == IListType2 doesn't equal while they're the same type.
What's going on?
This is ugly...
Now see what happens if you call IListType2.GetGenericTypeDefinition()... It recovers the type information!
It would be great that a .NET Framework development team member could explain us why an already generic type definition which has strangely lost its metadata has IsGenericTypeDefinition property set to false while it's still a generic type definition, and finally, if you call GetGenericTypeDefinition() on it, you recover the type information.
This is strange...
The following equality will be true:
Type IListType = new List<string>().GetType()
.GetInterface("IList`1")
.GetGenericTypeDefinition();
// Got interface is "like a generic type definition" since it has
// no type for T generic parameter, and once you call
// GetGenericTypeDefinition() again, it recovers the lost metadata
// and the resulting generic type definition equals the one got from
// List<string>!
Type IListType2 = typeof(List<>).GetInterface("IList`1").GetGenericTypeDefinition();
bool y = IListType == IListType2;
The following types are all different and not connected by an inheritance relationship:
IList<T>
IList<int>
IList<string>
All of these have different Type objects because you can do different things with them. The latter two are the specializations of the former. The first is the generic type definition (which you can obtain through GetGenericTypeDefinition).
There is another part to the explanation. When you say class List<T> : IList<T> then the IList<T> part is not equal to typeof(IList<>) because it is already specialized to T. This is no longer a generic type definition. It is a concrete type such as IList<int>. It is specialized to bind its only type argument to the T that List<T> was specialized to.
Experiment for LINQPad:
Type bound = new List<string>().GetType().GetInterface("IList`1");
bound.GenericTypeArguments.Single().Dump(); //string
Type bound = typeof(List<>).GetInterface("IList`1");
bound.GenericTypeArguments.Single().Dump(); //"T"
(bound.GenericTypeArguments.Single() == typeof(List<>).GetGenericArguments().Single()).Dump(); //true
The first version of IList<T> is the actual typed version of IList<T>, let's say IList<string>.
The second one is the generic definition of IList<T> without a type for T.
That makes the two interfaces different. There are not the same, since the first is a concrete version of the second.

C# -- Knowing a base type of a Generic Type

How can I find out the base type of a generic type?
For example
Func<A, B>
I'd like to be able to say this is a Func<> .. but apparently, Func<,> is different from Func<> -- Is there a way to somehow catch them both, or Func<,,,> etc?
You're looking for GetGenericTypeDefinition:
var t = typeof(Func<int, string>);
var tGeneric = t.GetGenericTypeDefinition();
Assert.AreEqual(typeof(Func<,>), tGeneric);
If you then want to know if a type is one of the many Func<> variants, then your best best is simply to do something like this. Checking type names, as suggested elsewhere is absolutely NOT the way to check type identity:
static Type[] funcGenerics = new[]{
typeof(Func<>), typeof(Func<,>), typeof(Func<,,>), typeof(Func<,,,>),
/* and so on... */
}
//there are other ways to do the above - but this is the most explicit.
static bool IsFuncType(Type t)
{
if(t.IsGenericTypeDefinition)
return funcGenerics.Any(tt => tt == t);
else if(t.IsGenericType)
return IsFuncType(t.GetGenericTypeDefinition());
return false;
}
Your terminology is incorrect - which I suspect why you got a downvote on your question. A base type is one that a type inherits from (not an interface, which is different, although conceptually very similar).
A generic type definition is best thought of as being like a template (the strong qualification there because the term 'template' is used in C++ and, while visually similar they are very different in implementation).
More accurately, Func<,> is a generic type definition whereas Func<int, string> is a closed generic (the 'generic type').
You can also have an open generic, which is where the type arguments are generic parameters - e.g, given:
class MyType<T> : List<T> { }
Then List<T> is an open generic with the generic type definition List<>, because T is a generic parameter which will not be closed till MyType<T> is referenced with a concrete type argument, such as int or string.
Finally, just because a bunch of generic types share the same common name, e.g. Func<>, Func<,>, and Func<,,> it does not mean they are in any way related. At the type level, there is no explicit connection, which is why you have to check for all these type identities, and why there is no common 'base' as you put it. If they all had a common interface or base class, however, then you could - by checking for compatibility with that interface or base type.
Given a generic type definition, you can construct generic types using MakeGenericType, as has been mentioned by Jeffrey Zhang.
No, you can't, There is not a base type of a Gerneric Type. If you want to get a specific generic type by type parameters, You can use MakeGenericType. For example:
//get Func<int, string> type
typeof(Func<>).MakeGenericType(typeof(int), typeof(string));
If you want to get a Generic Type from specified generic type, You can use GetGenericTypeDefinition. For example:
//get Func<,> type
typeof(Func<int, string>).GetGenericTypeDefinition();
It is Because Func< A, B > does not inherit from Func<> It is a generic based on Func<,>.
However, you will notice that
typeof(Func<int, int>).FullName // equals "System.Func`2...
typeof(Func<int, int, int>).FullName // equals "System.Func`3...
It is a bit ugly but you could use something like
YourType.FullName.StartsWith("System.Func")
Hope it helps
Edit:
Why not use YourType.GetGenericTypeDefinition()?
Because typeof(Func<int, int>).GetGenericTypeDefinition() returns Func<,>
and typeof(Func<int, int, int>).GetGenericTypeDefinition() return Func<,,>.
Func<,> and Func<,,> are not the same Type.

Are Generic Generic type parameters in C# allowed in some form

I would like to create the following class snippet
class Lookup<TKey,TValue,TCollection>
where TCollection : ICollection<>
{
public TCollection<TKey> _KeyCollection;
public TCollection<TValue> _ValueCollection;
}
Is this pattern in general possible in C#? In the current form the compiler does not like it. You can't seem to constrain a type parameter to be a generic. However it looks like it a reasonable thing to want to do. Is there any trick to achieve it?
Note: This question is specifically about generics and type constraints. It is not looking for a work around for what you think I might be trying to do in my wider application.
You can't constrain a generic type parameter to be an open generic type, which seems to be how you're attempting to use it in the rest of the class.
You asked for a spec reference, and there's not one place that seems to spell it out in a nice, concise manner.
The best I can find is in section 4.5 (from C# 5.0 spec):
As a type, type parameters are purely a compile-time construct. At run-time, each type parameter is bound to a run-time type that was specified by supplying a type argument to the generic type declaration. Thus, the type of a variable declared with a type parameter will, at run-time, be a closed constructed type (ยง4.4.2). The run-time execution of all statements and expressions involving type parameters uses the actual type that was supplied as the type argument for that parameter.
But in your attempt, TCollection won't match up with this text because it's asking for an non-closed type.
You can't have an open generic type as a constraint. You can, however, have a closed generic type:
class Lookup<TKey, TValue, TKeyCollection, TValueCollection>
where TKeyCollection : ICollection<TKey>
where TValueCollection : ICollection<TValue>
{
public TKeyCollection _KeyCollection;
public TValueCollection _ValueCollection;
}
It may not be pretty and there are a lot of type parameters, but it is possible.

Casting generic type in linq query

So I have class which accepts a generic type parameter and does a little special handling if the type parameter is a subclass of a given type.
IEnumerable<T> models = ...
// Special handling of MySpecialModel
if (filterString != null && typeof(MySpecialModel).IsAssignableFrom(typeof(T)))
{
var filters = filterString.Split(...);
models =
from m in models.Cast<MySpecialModel>()
where (from t in m.Tags
from f in filters
where t.IndexOf(f, StringComparison.CurrentCultureIgnoreCase) >= 0
select t)
.Any()
select (T)m;
}
But I'm getting an exception on the last line
Cannot convert type 'MySpecialModel' to 'T'
If I change the code to use as instead of casting, I get this error.
The type parameter 'T' cannot be used with the 'as' operator because it does not have a class type constraint nor a 'class' constraint.
What am I missing here?
Update
This class needs can take any type parameter, including structs and built-in types, so a generic constraint would not be a suitable solution in my case.
Do Select(x => (MySpecialModel)x)
The LINQ Cast<T> method will only work for casting elements to that the element already is (such as a base type, derived type, or interface). It is not intended to cast objects that are able to be cast to a target type. (e.g. new List<int>{1,2,3}.Cast<long>() will throw an exception as well.
The above answer wasn't wrong, but it doesn't address the question.
Just because you have proved with reflection that a generic parameter is bound to a given type, doesn't mean that the compiler knows that it is. In order to make this work, you will need to cast your T instance to a common type (e.g. object), then cast it to the specific type. e.g. (changing the last line in your query to select (T)(object)m should do the trick.
Try the following
select (T)(object)m;
At runtime you've verified that T is a subtype of MySpecialModel but the compiler doesn't have access to this information at compile time. It just sees an attempted conversion between 2 unrelated types: T and MySpecialModel.
To work around this you need to use object as a middle man. The compiler understands how to convert MySpecialModel to object and to go from object to T.
The most straightforward fix is to cast to object first before the cast to T:
select (T)(object)m;
The problem is your check occurs at runtime, but the compiler doesn't know that T must be an instance of MySpecialModel within the if statement. Therefore it just sees you are trying to cast to some arbitrary type T from MySpecialModel which is not safe, hence the error.
If you know that the generic type will always be a class, you can add a type constraint on your class:
public class Test<T> where T : class {}
Otherwise perform a double cast via object as smartcaveman has suggested:
.Select(x => (T)(object)x);
To use the as keyword, put the class constraint on your generic parameter:
void MyMethod<T>(T item) where T : class
{
//...
}
You might apply Nullable<T> constraint - that should enable the possibility to cast (at least using "as").

Restrict a generic type

I want to restrict the generic type parameter to:
1) either that of a certain user defined reference type;
OR
2) any of the primitive types in the CLR;
How do I say something to the effect of:
interface IDataManager<T>: IDataManager
where T: IDataObject, T: ValueType
There's no constraint you can use that limits you to the built-in primitives. What I would do to get around that is overload the method for each primitive, and perhaps have each overload simply pass it's argument to a private generic method that holds the common code.
From Constraint cannot be special class 'System.Enum'
More investigation shows the C# 2.0
specification to have the following
comments on constraints:
A class-type constraint must satisfy
the following rules:
The type must be a class type.
The type must not be sealed.
The type must not be one of the following types: System.Array,
System.Delegate, System.Enum, or
System.ValueType.
The type must not be object. Because all types derive from
object, such a constraint would
have no effect if it were
permitted.
At most one constraint for a given type parameter can be a class type.
Also Compiler Error CS0702
And mentioned at
Jon Skeet: Coding Blog : Generic constraints for enums and delegates
T: ValueType
The closest you can get is T : struct, but that wouldn't limit it only to CLR types. Either way, I don't believe there is a way to have an OR generic constraint. You could have one generic method and n-overloads for specific types.
You also can not, for example, define multiple generic functions differing only in their constraints. Constraints are not part of the signature. See: http://blogs.msdn.com/ericlippert/archive/2009/12/10/constraints-are-not-part-of-the-signature.aspx

Categories

Resources