This question already has answers here:
Get a generic method without using GetMethods
(10 answers)
Closed 9 years ago.
I'm trying to retrieve MethodInfo for Where method of Enumerable type:
typeof (Enumerable).GetMethod("Where", new Type[] {
typeof(IEnumerable<>),
typeof(Func<,>)
})
but get null. What am I doing wrong?
That previous answer works for some cases, however:
It doesn't handle nested generic types, such as a parameter type of Action<IEnumerable<T>>. It will treat all Action<> as matches, for example, string.Concat(IEnumerable<string>) and string.Concat<T>(IEnumerable<T>) will both match if searching for "Concat" with type IEnumerable<> on the string type. What is really desirable is handling nested generic types recursively, while treating all generic parameters as matching each other regardless of name while NOT matching concrete types.
It returns the first method matched rather than throwing an exception if the result is ambiguous, like type.GetMethod() does. So, you might get the method you wanted if you're lucky, or you might not.
Sometimes it will be necessary to specify BindingFlags in order to avoid ambiguity, such as when a derived class method 'hides' a base class method. You normally want to find base class methods, but not in a specialized case where you know the method you're looking for is in the derived class. Or, you might know you're looking for a static vs instance method, public vs private, etc. and don't want to match if it's not exact.
It doesn't address another major fault with type.GetMethods(), in that it also doesn't search base interfaces for methods when looking for a method on an interface type. OK, maybe that's being picky, but it's another major flaw in GetMethods() that has been a problem for me.
Calling type.GetMethods() is inefficient, type.GetMember(name, MemberTypes.Method, ...) will return only methods with a matching name instead of ALL methods in the type.
As a final nit-pick, the name GetGenericMethod() could be misleading, since you might be trying to find a non-generic method that happens to have a type parameter somewhere in a parameter type due to a generic declaring type.
Here's a version that addresses all of those things, and can be used as a general-purpose replacement for the flawed GetMethod(). Note that two extension methods are provided, one with BindingFlags and one without (for convenience).
/// <summary>
/// Search for a method by name and parameter types.
/// Unlike GetMethod(), does 'loose' matching on generic
/// parameter types, and searches base interfaces.
/// </summary>
/// <exception cref="AmbiguousMatchException"/>
public static MethodInfo GetMethodExt( this Type thisType,
string name,
params Type[] parameterTypes)
{
return GetMethodExt(thisType,
name,
BindingFlags.Instance
| BindingFlags.Static
| BindingFlags.Public
| BindingFlags.NonPublic
| BindingFlags.FlattenHierarchy,
parameterTypes);
}
/// <summary>
/// Search for a method by name, parameter types, and binding flags.
/// Unlike GetMethod(), does 'loose' matching on generic
/// parameter types, and searches base interfaces.
/// </summary>
/// <exception cref="AmbiguousMatchException"/>
public static MethodInfo GetMethodExt( this Type thisType,
string name,
BindingFlags bindingFlags,
params Type[] parameterTypes)
{
MethodInfo matchingMethod = null;
// Check all methods with the specified name, including in base classes
GetMethodExt(ref matchingMethod, thisType, name, bindingFlags, parameterTypes);
// If we're searching an interface, we have to manually search base interfaces
if (matchingMethod == null && thisType.IsInterface)
{
foreach (Type interfaceType in thisType.GetInterfaces())
GetMethodExt(ref matchingMethod,
interfaceType,
name,
bindingFlags,
parameterTypes);
}
return matchingMethod;
}
private static void GetMethodExt( ref MethodInfo matchingMethod,
Type type,
string name,
BindingFlags bindingFlags,
params Type[] parameterTypes)
{
// Check all methods with the specified name, including in base classes
foreach (MethodInfo methodInfo in type.GetMember(name,
MemberTypes.Method,
bindingFlags))
{
// Check that the parameter counts and types match,
// with 'loose' matching on generic parameters
ParameterInfo[] parameterInfos = methodInfo.GetParameters();
if (parameterInfos.Length == parameterTypes.Length)
{
int i = 0;
for (; i < parameterInfos.Length; ++i)
{
if (!parameterInfos[i].ParameterType
.IsSimilarType(parameterTypes[i]))
break;
}
if (i == parameterInfos.Length)
{
if (matchingMethod == null)
matchingMethod = methodInfo;
else
throw new AmbiguousMatchException(
"More than one matching method found!");
}
}
}
}
/// <summary>
/// Special type used to match any generic parameter type in GetMethodExt().
/// </summary>
public class T
{ }
/// <summary>
/// Determines if the two types are either identical, or are both generic
/// parameters or generic types with generic parameters in the same
/// locations (generic parameters match any other generic paramter,
/// but NOT concrete types).
/// </summary>
private static bool IsSimilarType(this Type thisType, Type type)
{
// Ignore any 'ref' types
if (thisType.IsByRef)
thisType = thisType.GetElementType();
if (type.IsByRef)
type = type.GetElementType();
// Handle array types
if (thisType.IsArray && type.IsArray)
return thisType.GetElementType().IsSimilarType(type.GetElementType());
// If the types are identical, or they're both generic parameters
// or the special 'T' type, treat as a match
if (thisType == type || ((thisType.IsGenericParameter || thisType == typeof(T))
&& (type.IsGenericParameter || type == typeof(T))))
return true;
// Handle any generic arguments
if (thisType.IsGenericType && type.IsGenericType)
{
Type[] thisArguments = thisType.GetGenericArguments();
Type[] arguments = type.GetGenericArguments();
if (thisArguments.Length == arguments.Length)
{
for (int i = 0; i < thisArguments.Length; ++i)
{
if (!thisArguments[i].IsSimilarType(arguments[i]))
return false;
}
return true;
}
}
return false;
}
Note that the IsSimilarType(Type) extension method can be made public and might be useful on its own. I know, the name isn't great - you're welcome to come up with a better one, but it might get really long to explain what it does. Also, I added yet another improvement by checking for 'ref' and array types (refs are ignored for matching, but arrays dimensions must match).
So, that's how Microsoft should have done it. It's really not that hard.
Yeah, I know, you can shorten some of that logic using Linq, but I'm not a huge fan of Linq in low-level routines like this, and also not unless the Linq is about as easy to follow as the original code, which is often NOT the case, IMO.
If you love Linq, and you must, you can replace the inner-most part of IsSimilarType() with this (turns 8 lines into 1):
if (thisArguments.Length == arguments.Length)
return !thisArguments.Where((t, i) => !t.IsSimilarType(arguments[i])).Any();
One last thing: If you're looking for a generic method with a generic parameter, such as Method<T>(T, T[]), you'll have to find a Type which is a generic parameter (IsGenericParameter == true) to pass in for the parameter type (any one will do, because of the 'wildcard' matching). However, you can't just do new Type() - you have to find a real one (or build one with TypeBuilder). To make this easier, I added the public class T declaration, and added logic to IsSimilarType() to check for it and match any generic parameter. If you need a T[], just use T.MakeArrayType(1).
Unfortunately, generics are not well-supported in .NET Reflection. In this particular case, you'll need to call GetMethods and then filter the result set for the method you're looking for. An extension method like the following should do the trick.
public static class TypeExtensions
{
private class SimpleTypeComparer : IEqualityComparer<Type>
{
public bool Equals(Type x, Type y)
{
return x.Assembly == y.Assembly &&
x.Namespace == y.Namespace &&
x.Name == y.Name;
}
public int GetHashCode(Type obj)
{
throw new NotImplementedException();
}
}
public static MethodInfo GetGenericMethod(this Type type, string name, Type[] parameterTypes)
{
var methods = type.GetMethods();
foreach (var method in methods.Where(m => m.Name == name))
{
var methodParameterTypes = method.GetParameters().Select(p => p.ParameterType).ToArray();
if (methodParameterTypes.SequenceEqual(parameterTypes, new SimpleTypeComparer()))
{
return method;
}
}
return null;
}
}
With this in hand the following code will work:
typeof(Enumerable).GetGenericMethod("Where", new Type[] { typeof(IEnumerable<>), typeof(Func<,>) });
Related
I have a list of types (System.Type) which need te be queried on the database.
For each of this types, I need to call the following extensionmethod (which is part of LinqToNhibernate):
Session.Linq<MyType>()
However I do not have MyType, but I want to use a Type instead.
What I have is:
System.Type typeOne;
But I cannot perform the following:
Session.Linq<typeOne>()
How can I use a Type as a Generic parameter?
You can't, directly. The point of generics is to provide compile-time type safety, where you know the type you're interested in at compile-time, and can work with instances of that type. In your case, you only know the Type so you can't get any compile-time checks that any objects you have are instances of that type.
You'll need to call the method via reflection - something like this:
// Get the generic type definition
MethodInfo method = typeof(Session).GetMethod("Linq",
BindingFlags.Public | BindingFlags.Static);
// Build a method with the specific type argument you're interested in
method = method.MakeGenericMethod(typeOne);
// The "null" is because it's a static method
method.Invoke(null, arguments);
If you need to use this type a lot, you might find it more convenient to write your own generic method which calls whatever other generic methods it needs, and then call your method with reflection.
To do this you need to use reflection:
typeof(Session).GetMethod("Linq").MakeGenericMethod(typeOne).Invoke(null, null);
(assuming that Linq<T>() is a static method on the type Session)
If Session is actually an object, you'll need to know where the Linq method is actually declared, and pass in Session as an argument:
typeof(DeclaringType).GetMethod("Linq").MakeGenericMethod(typeOne)
.Invoke(null, new object[] {Session});
I have one general method which call Call Generic Method Through Reflection
/// <summary>
/// This method call your method through Reflection
/// so i wil call the method like CallGenericMethodThroughReflection<Session>(assemblyQualifiedName,Linq,false,new[] { file })
/// </summary>
/// <typeparam name="T">Call method from which file</typeparam>
/// <param name="assemblyQualifiedName">Your can get assemblyQualifiedName like typeof(Payroll.Domain.Attendance.AttendanceApplicationMaster).AssemblyQualifiedName</param>
/// <param name="methodName"></param>
/// <param name="isStaticMethod"></param>
/// <param name="paramaterList"></param>
/// <param name="parameterType">pass parameter type list in case of the given method have overload </param>
/// <returns>return object of calling method</returns>
public static object CallGenericMethodThroughReflection<T>(string assemblyQualifiedName, string methodName,bool isStaticMethod ,object[] paramaterList,Type[] parameterType = null)
{
try
{
object instance = null;
var bindingAttr = BindingFlags.Static | BindingFlags.Public;
if (!isStaticMethod)
{
instance = Activator.CreateInstance<T>();
bindingAttr = BindingFlags.Instance | BindingFlags.Public;
}
MethodInfo MI = null;
var type = Type.GetType(assemblyQualifiedName);
if(parameterType == null)
MI = typeof(T).GetMethod(methodName, bindingAttr);
else
MI = typeof(T).GetMethod(methodName, bindingAttr,null, parameterType, null);//this will work in most case some case not work
if (type == null || MI == null) // if the condition is true it means given method or AssemblyQualifiedName entity not found
return null;
var genericMethod = MI.MakeGenericMethod(new[] { type });
return genericMethod.Invoke(instance, paramaterList);
}
catch (Exception ex)
{
throw ex;
}
}
I know this is most probably a simple question involving Generics, or, quite possibly, just a big "no-no" overall, but I'm curious to see if I can get this working.
I'm trying to create a method that takes in an object and, if the object is an enumerable type, to pass it off to a method that can work on any kind of enumerable, but I'm getting terribly stuck.
My current code for the method that takes in an object looks something like the following:
private void MyMethod()
{
Dictionary<string, object> MyVals = new Dictionary<string,object>();
... fill the MyVals dictionary ...
object x = MyVals["Val_1"];
Type type = x.GetType();
// Check if we have a series of values rather than a single value
if (type != typeof(string) && typeof(IEnumerable).IsAssignableFrom(type))
Test(x);
}
Then, I thought I could write something like one of the following as the method signature for my Test method:
1. Write it as if it's an IEnumerable of object:
private void Test(IEnumerable<object> MyEnumeration)
Tried calling it via:
Test((IEnumerable<object>)x);
Leads to run-time error that it cannot cast from IEnumerable<int> to IEnumerable<object>
2. Try using Generics:
private void Test<T>(IEnumerable<T> MyEnumeration)
Tried calling it via:
Test(x);
Leads to design-time error that the signature is incorrect / invalid arguments.
or via:
Test<type>(x);
Leads to a design-time error that the type or namespace type could not be found.
How could this be done OR is it just bad programming practice and there is a better way to do this?
Thanks!
The problem is with this code:
if (type != typeof(string) && typeof(IEnumerable).IsAssignableFrom(type))
Test(x);
You now know that x is an IEnumerable, but it still treated as an object when the compiler determines which method signatures are compatible.
If you did this:
if (type != typeof(string) && typeof(IEnumerable).IsAssignableFrom(type))
{
IEnumerable asEnumerable = (IEnumerable)x;
Test(asEnumerable);
}
Then it can be passed to
void Test(IEnumerable t)
{
}
Or, if you really want to use IEnumerable<object>:
if (type != typeof(string) && typeof(IEnumerable).IsAssignableFrom(type))
{
IEnumerable<object> asEnumerable = ((IEnumerable)x).Cast<object>();
Test(asEnumerable);
}
And, if you want an IEnumerable<T>, see Jon Skeet's answer to a different question.
You should create two overloads of Test() method. one which would handle IEnumerable and another which handles IEnumerable<T>. Why do you need method overload? Because IEnumerables are not generic IEnumerable<T> and at run time you won't be knowing the type of generic type to use Cast on object.
Below is the sample showing how exactly you can achieve what you're looking for:
public void MyMethod()
{
// Sample object with underlying type as IEnumerable
object obj1 = new ArrayList();
// Sample object with underlying type as IEnumerable & IEnumerable<T>
object obj2 = (IList<string>)new List<string>();
if (typeof(IEnumerable).IsAssignableFrom(obj1.GetType()))
{
if (!obj1.GetType().IsGenericType)
{
// Handles case of IEnumerable
Test((IEnumerable)obj1);
}
else
{
// Handles case of IEnumerable<T>
InvokeGenericTest(obj2);
}
}
}
public void Test(IEnumerable source)
{
Console.WriteLine("Yes it was IEnumerable.");
}
public void Test<T>(IEnumerable<T> source)
{
Console.WriteLine("Yes it was IEnumerable<{0}>.", typeof(T));
// Use linq with out worries.
source.ToList().ForEach(x => Console.WriteLine(x));
}
/// <summary>
/// Invokes the generic overload of Test method.
/// </summary>
/// <param name="source"></param>
private void InvokeGenericTest(object source)
{
Type t = source.GetType();
var method = this.GetType().GetMethods().Where(x => x.IsGenericMethod && x.Name == "Test").First();
var genericMethod = method.MakeGenericMethod(t.GenericTypeArguments.First());
genericMethod.Invoke(this, new object[] { source });
}
This has a bad code smell so I would elaborate what it is you actually want to do and maybe someone can help. Your runtime error is due to the fact the collection you have is in fact NOT an IEnumerable[object] but is IEnumerable[int]. You would have to call .Cast<object> first.
So it would be a double cast.
Test(((IEnumerable<int>)x).Cast<object>);
Again, this has a terrible code smell. You should elaborate how the data is coming in and I am sure someone can help you.
What is the difference between Type.IsGenericType and Type.IsGenericTypeDefinition ? Interestingly enough, MSDN's link for IsGenericTypeDefinition is broken.
Update:
IsGenericTypeDefinition MSDN's entry
After playing a bit with trying to retrieve all the DbSets defined in a given DbContext, I was lead to the following, which behavior I am trying to understand: filtering properties via IsGenericType returns the desired results, while with IsGenericTypeDefinition not (does not return any).
It's interesting that from this post I have the impression that the author did get his DbSets using IsGenericTypeDefinition, while I did not.
Follows a sample that illustrates the discussion:
private static void Main(string[] args)
{
A a = new A();
int propertyCount = a.GetType().GetProperties().Where(p => p.PropertyType.IsGenericType).Count();
int propertyCount2 = a.GetType().GetProperties().Where(p => p.PropertyType.IsGenericTypeDefinition).Count();
Console.WriteLine("count1: {0} count2: {1}", propertyCount, propertyCount2);
}
// Output: count1: 1 count2: 0
public class A
{
public string aaa { get; set; }
public List<int> myList { get; set; }
}
IsGenericType tells you that this instance of System.Type represents a generic type with all its type parameters specified. For example, List<int> is a generic type.
IsGenericTypeDefinition, on the other hand, tells you that this instance of System.Type represents a definition from which generic types can be constructed by supplying type arguments for its type parameters. For example, List<> is a generic type definition.
You can get a generic type definition of a generic type by calling GetGenericTypeDefinition:
var listInt = typeof(List<int>);
var typeDef = listInt.GetGenericTypeDefinition(); // gives typeof(List<>)
You can make a generic type from a generic type definition by providing it with type arguments to MakeGenericType:
var listDef = typeof(List<>);
var listStr = listDef.MakeGenericType(typeof(string));
(This answer compares all of the generic-type-related properties of Type in a side-by-side table below, so if you're already familiar with .NET's generics and just want a reference then just scroll down to the table)
First, remember the difference between parameters and arguments, especially w.r.t. generic type parameters and generic type arguments (and also generic method type parameters and generic method type arguments):
A generic type parameter is the declared type "placeholder" in an "open" generic type.
For example, in class Generic<T0,T1> {}, the T0 and T1 symbols are the generic type parameters. Note that when simply given a generic class definition that's unused then there's no type arguments.
A generic type argument is the type-identifier specified for a generic type parameter by a consumer of a generic class.
For example, in Generic<String,Object> gen = new Generic<String,Object> then...
...the generic type argument for generic type parameter T0 is String.
...the generic type argument for generic type parameter T1 is Object.
However, generic type arguments don't need to be concrete types: they can be a generic type parameter from the consumer's context.
For example, in class Generic<TItem> { public Object Foo() { return new List<TItem>(); } }
...then (inside the Foo method) the class Generic<TItem>'s generic type parameter TItem is used as the generic type argument for List<T>'s generic type parameter T.
Yes, if you get confused by all that don't worry because that's normal.
Finally, generic method type parameters and generic method type arguments work the same way as generic type parameters and generic type arguments, respectively, except they're scoped to a single method:
For example, the class NotGenericClass in class NotGenericClass { void GenericMethod<T>() { } } does not have any generic type parameters, but its method GenericMethod<T>() does have a single generic method type parameter - and if GenericMethod is never ever actually called/used/invoked then GenericMethod will not have any generic method type arguments as those only exist at generic instantiation sites (i.e. at the point of generic instantiation).
Given these C# classes...
class NormalClass { }
class Generic<T> { }
class Derived : Generic<String> { }
class HasGenericMethod { public void Foo<T>() {} }
...and these Type instances from GetGenericArguments():
Type[] genericTypeArgs = typeof(Generic<>).GetGenericArguments();
Type genTypeArg = genericTypeArgs.Single();
Type[] genericMethodTypeArgs = typeof(HasGenericMethod).GetMethod( nameof(HasGenericMethod.Foo) ).GetGenericArguments();
Type genMethodArg = genericMethodTypeArgs.Single();
...then their typeof() expressions will have these properties:
Example
typeof(NormalClass)
typeof(Generic<>)
typeof(Generic<String>)
typeof(Derived)
genTypeArg
genMethodArg
typeof(Generic<String>[])
Type properties
Type.IsTypeDefinition
Yes
Yes
No
Yes
No
No
No
Type.IsGenericType
No
Yes
Yes
No
No
No
No5
Type.ContainsGenericParameters
No
Yes
No
No
Yes4
Yes4
No
Type.GenericTypeArguments
Empty
Empty
{ typeof(String) }
Empty
Empty
Empty
Empty
Type.IsConstructedGenericType
No
No
Yes
No
No
No
No
Type.IsGenericTypeDefinition
No
Yes
No
No
No
No
No
Generic parameter properties:
Type.IsGenericParameter
No
No
No
No
Yes
Yes
No
Type.IsGenericMethodParameter
No
No
No
No
No
Yes
No
Type.IsGenericTypeParameter
No
No
No
No
Yes
No
No
Methods:
Type.GetGenericArguments()
Empty
{ typeof(T) }
{ typeof(String) }
Empty
Empty
Empty
{ typeof(String) }
Type.GetGenericParameterConstraints()
Exception1
Exception1
Exception1
Exception1
Empty
Empty
Exception1
Type.GetGenericTypeDefinition()
Exception2
typeof(Generic<>)
typeof(Generic<>)
Exception2
Exception2
Exception2
Exception2
You can generate this table yourself (albiet, transposed) using this LinqPad script.
As a reminder to myself: if you have a Type object from Object.GetType() for an object that may be either a closed generic type (i.e. Object.GetType().IsConstructedGenericType == true), or is a non-generic type derived from that generic type, and you want to find out what, do this:
private static readonly Type _knownGenericType = typeof(Generic<>);
public static Boolean TryGetTypeArgsOfKnownGenericType( Object obj, [NotNullWhen(true)] out Type? actualArgType )
{
Type t = obj.GetType();
while( t != null )
{
if t.IsConstructedGenericType && t.GetGenericTypeDefinition() == _knownGenericType )
{
Type[] tArgs = t.GetGenericArguments();
actualArgType = tArgs.Single();
return true;
}
t = t.BaseType;
}
actualArgType = null;
return false;
}
So this code below will print "Sucess: T := System.String" twice:
if( TryGetTypeArgsOfKnownGenericType( new Derived(), out Type? tArg ) )
{
Console.WriteLine("Success: T := " + tArg.FullName);
}
if( TryGetTypeArgsOfKnownGenericType( new Generic<String>(), out Type? tArg ) )
{
Console.WriteLine("Success: T := " + tArg.FullName);
}
Footnotes:
InvalidOperationException: "Method may only be called on a Type for which Type.IsGenericParameter is true."
InvalidOperationException: "This operation is only valid on generic types."
typeof(T) is typeof(Generic<>).GetGenericArguments().Single()
It's surprising that typeof(T).ContainsGenericParameters == true when T is a generic type parameter without an argument set (i.e. T is undefined), so I'd have expected an InvalidOperationException to be thrown instead.
The documentation for ContainsGenericParameters seemingly justifies returning true (emphasis mine):
For convenience and to reduce the chance of error, the ContainsGenericParameters property provides a standard way to distinguish between closed constructed types, which can be instantiated, and open constructed types, which cannot. If the ContainsGenericParameters property returns true, the type cannot be instantiated.
Apparently using typeof(T[]) when T is a constructed generic type: the ContainsGenericParameters property is false but the GetGenericArguments() method returns a non-empty array of the type-arguments of T instead of the type-arguments of System.Array (which isn't actually a generic type).
For example:
typeof(Generic<String>[]).IsGenericType == false
typeof(Generic<String>[]).GetGenericArguments() == new[] { typeof(String) }
This is documented in the rightmost column of the above table.
And described in the documentation:
The ContainsGenericParameters property searches recursively for type parameters. For example, it returns true for an array whose elements are type A<T> even though the array is not itself generic. Contrast this with the behavior of the IsGenericType property, which returns false for arrays.
I had expected to find an answer easily to this problem, but I didn't.
I'd like to know if it is possible to determine whether a method has the keyword 'override' attributed to it, given its instance of MethodInfo.
I was thinking maybe the following would achieve that:
/// <summary> Returns whether the specified methodInfo is attributed with the keyword 'override'. </summary>
public static bool IsOverriding(this MethodInfo methodInfo)
{
if (methodInfo == null) throw new ArgumentNullException();
return methodInfo.DeclaringType != methodInfo.GetBaseDefinition().DeclaringType;
}
I've sucessfully tested some non-virtual, virtual and abstract examples, but I feel like I'm missing some scenarios, maybe with hiding or generics(although I can't figure out how that would come into play).
I try to find this thing also. From the question, you give the idea to get IsOverriding work for override keyword. However for hiding I try to create IsHiding for keyword new. Here is the basic C# OOP:
override only used when the based method contain abstract or virtual modifier.
new is used to hide based method with the same name but...
new cannot be apply to abstract base method because it generate compiler error.
However the interesting part is new also can be applied to method if the base method contain virtual.
The inseresting part is we can hide or override virtual method. We know that GetBaseDefinition() will return the base MethodInfo if we override a virtual method. but the key to differentiate it is the GetBaseDefinition() will return the same MethodInfo instead of it base MethodInfo if we hiding virtual method.
override keyword is a must while new is only used to suppress the warning message. So we can diffrentiate override and new by the IsAbstract and IsVirtual combine with DeclaringType and BaseType.
public static bool IsOverriding(this MethodInfo methodInfo)
{
if (methodInfo == null) throw new ArgumentNullException("methodInfo");
return methodInfo.DeclaringType != methodInfo.GetBaseDefinition().DeclaringType;
}
public static bool IsHiding(this MethodInfo methodInfo)
{
if (methodInfo == null) throw new ArgumentNullException("methodInfo");
if (methodInfo.DeclaringType == methodInfo.GetBaseDefinition().DeclaringType)
{
var baseType = methodInfo.DeclaringType.BaseType;
if (baseType != null)
{
MethodInfo hiddenBaseMethodInfo = null;
var methods = baseType.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy | BindingFlags.Instance | BindingFlags.Static);
foreach (var mi in methods)
if (mi.Name == methodInfo.Name)
{
var miParams = mi.GetParameters();
var methodInfoParams = methodInfo.GetParameters();
if (miParams.Length == methodInfoParams.Length)
{
var i = 0;
for (; i < miParams.Length; i++)
{
if (miParams[i].ParameterType != methodInfoParams[i].ParameterType
|| ((miParams[i].Attributes ^ methodInfoParams[i].Attributes).HasFlag(ParameterAttributes.Out))) break;
// Simplified from:
//if (miParams[i].ParameterType != methodInfoParams[i].ParameterType
// || (miParams[i].Attributes.HasFlag(ParameterAttributes.Out) && !methodInfoParams[i].Attributes.HasFlag(ParameterAttributes.Out))
// || !(miParams[i].Attributes.HasFlag(ParameterAttributes.Out) && methodInfoParams[i].Attributes.HasFlag(ParameterAttributes.Out))) break;
}
if (i == miParams.Length)
{
hiddenBaseMethodInfo = mi;
break;
}
}
}
if (hiddenBaseMethodInfo != null && !hiddenBaseMethodInfo.IsPrivate) return true;
}
}
return false;
}
I test it using the simple inheritance and it works. I don't think about generic method..yet..
EDIT: I just change the code above because I forgot the idea about inherited type can contain newly declared method which is should not threated as new. IsHiding() will first make sure it have the same DeclaringType (it seems like new) but need to look at the base declaring types by DeclaringType.BaseType if a method with the same name exist.
Note that because of there is no BindingFlags.DeclaredOnly, GetMethod() will search through the entire base types, so no need to recursively search to each base types. BindingFlags.FlattenHierarchy is used to include static method in abstract-abstract base class like this:
public abstract class A
{
public static void Stat() { }
}
public abstract class B : A
{
}
public class C: B
{
public new static void Stat() { }
}
EDIT: I just fix the IsHiding() above to check for base method overloads and prevent AmbiguousMatchException by using GetMethods() instead of GetMethod(). The overloads tested to work with combination with different parameters, mix with ref, out, params and optional parameter. Signature for overloads being compared based on parameter count, parameter types and its modifier:
ref or out included in the signature but both cannot be overloaded to each other.
return type, optional (default value) and params on right most parameter should be ignored
ref compared by the ParameterType itself (see the ending '&' during debugging) and no need to compare by the IsByRef while the out compared by the Attributes flag. I am using simplified expression bitwise XOR to skip the loop if and only if one of the attributes has flag Out which makes the signature different. Don't confused with HasFlag in .NET 4, it just want to make sure Out bit is 1 by the XOR result.
Well, I don't see how that would come into play either. Your code there does indeed determine whether a method is defined or overriden.
In the case of hiding, the declaring type is the one that hides the method via new.
In the case of generics, all methods are defined by the template class.
You can try this
public static bool IsOverriding(this MethodInfo methodInfo)
{
if (methodInfo == null) throw new ArgumentNullException();
return methodInfo.GetBaseDefinition() != methodInfo;
}
I want to determine if a generic object type ("T") method type parameter is a collection type. I would typically be sending T through as a Generic.List but it could be any collection type as this is used in a helper function.
Would I be best to test if it implements IEnumerable<T>?
If so, what would the code look like?
Update 14:17 GMT+10 Possibly extending on a solution here (however only works for List<T>'s not IEnumerable<T>'s when it should if List derives ?)
T currentObj;
// works if currentObj is List<T>
currentObj.GetType().GetGenericTypeDefinition() == typeof(List<>)
// does not work if currentObj is List<T>
currentObj.GetType().GetGenericTypeDefinition() == typeof(IEnumerable<>)
This will be the simplest check..
if(Obj is ICollection)
{
//Derived from ICollection
}
else
{
//Not Derived from ICollection
}
You can use Type.GetInterface() with the mangled name.
private bool IsTAnEnumerable<T>(T x)
{
return null != typeof(T).GetInterface("IEnumerable`1");
}
In order to get the actual type of T at runtime, you can use the typeof(T) expression. From there the normal type comparison operators will do the trick
bool isEnumerable = typeof(IEnumerable<int>).IsAssignableFrom(typeof(T));
Full Code Sample:
static bool Foo<T>()
{
return typeof(IEnumerable<int>).IsAssignableFrom(typeof(T));
}
Foo<List<T>>(); // true
Foo<int>(); // false
Personally I tend to use a method that I wrote myself, called TryGetInterfaceGenericParameters, which I posted below. Here is how to use it in your case:
Example of use
object currentObj = ...; // get the object
Type[] typeArguments;
if (currentObj.GetType().TryGetInterfaceGenericParameters(typeof(IEnumerable<>), out typeArguments))
{
var innerType = typeArguments[0];
// currentObj implements IEnumerable<innerType>
}
else
{
// The type does not implement IEnumerable<T> for any T
}
It is important to note here that you pass in typeof(IEnumerable<>), not typeof(IEnumerable) (which is an entirely different type) and also not typeof(IEnumerable<T>) for any T (if you already know the T, you don’t need this method). Of course this works with any generic interface, e.g. you can use typeof(IDictionary<,>) as well (but not typeof(IDictionary)).
Method source
/// <summary>
/// Determines whether the current type is or implements the specified generic interface, and determines that
/// interface's generic type parameters.</summary>
/// <param name="type">
/// The current type.</param>
/// <param name="interface">
/// A generic type definition for an interface, e.g. typeof(ICollection<>) or typeof(IDictionary<,>).</param>
/// <param name="typeParameters">
/// Will receive an array containing the generic type parameters of the interface.</param>
/// <returns>
/// True if the current type is or implements the specified generic interface.</returns>
public static bool TryGetInterfaceGenericParameters(this Type type, Type #interface, out Type[] typeParameters)
{
typeParameters = null;
if (type.IsGenericType && type.GetGenericTypeDefinition() == #interface)
{
typeParameters = type.GetGenericArguments();
return true;
}
var implements = type.FindInterfaces((ty, obj) => ty.IsGenericType && ty.GetGenericTypeDefinition() == #interface, null).FirstOrDefault();
if (implements == null)
return false;
typeParameters = implements.GetGenericArguments();
return true;
}
Also, remember just because you are using generics, don't forget other basic techniques, in this case, like overloading. I suspect the you are planning something like this:
void SomeFunc<T>(T t)
{
if (IsCollectionCase(t))
DoSomethingForCollections()
else
DoSOmethingElse();
}
This would be far better handled as:
void SomeFunc(IEnumerable t)
{
DoSomethingForCollections()
}
void SomeFunc<T>(T t)
{
DoSomethingElse()
}
I would test IEnumerable instead, since a collection type could implement only IEnumerable, it doesn't have to implement IEnumerable<T>.
It also depends: what do you mean with collection type? You could have a collection without implementing any of those interfaces.
While I can't be certain what the original poster's intent was, there have been several responses to the effect of casting to IEnumerable for testing. That's fine, but everyone should be aware that string instances pass this test, which may not be something the original author intended. I know I certainly didn't when I went looking for an answer and found this post:
string testString = "Test";
Console.WriteLine(testString as IEnumerable != null); // returns true
I am in the process of trying to write a custom serializer that uses reflection to accomplish certain tasks. As part of a task, I need to determine if a property value is a collection/array/list of items or a single property value. What is particularly annoying is that several Linq expressions actually result in an enumerable type value, but GetType().IsArray returns false for these, and casting them to ICollection returns null as well, but casting them to IEnumerable returns a non-null value.
So...for the time being, I am still seeking a solution that works for all cases.
For simplicity and code sharing, I usually use this extension method:
public static bool IsGenericList(this object obj)
{
return IsGenericList(obj.GetType());
}
public static bool IsGenericList(this Type type)
{
if (type == null)
{
throw new ArgumentNullException("type");
}
foreach (Type #interface in type.GetInterfaces())
{
if (#interface.IsGenericType)
{
if (#interface.GetGenericTypeDefinition() == typeof(ICollection<>))
{
// if needed, you can also return the type used as generic argument
return true;
}
}
}
return (type.GetInterface("IEnumerable") != null);
}
If you want to do a check and get true for any list/collection/IEnumerable, but get false for type of string, then
private static bool IsIEnumerable(Type requestType)
{
var isIEnumerable = typeof(IEnumerable).IsAssignableFrom(requestType);
var notString = !typeof(string).IsAssignableFrom(requestType);
return isIEnumerable && notString;
}
I came across the same issue while attempting to serialize any object to JSON format. Here is what I ended up using:
Type typ = value.GetType();
// Check for array type
if(typeof(IEnumerable).IsAssignableFrom(typ) || typeof(IEnumerable<>).IsAssignableFrom(typ))
{
List<object> list = ((IEnumerable)value).Cast<object>().ToList();
//Serialize as an array with each item in the list...
}
else
{
//Serialize as object or value type...
}
I love generics. In this method T must have a public and parameterless constructor which means you can not use IList<object> for T. You must use List<object>
public static T IsEnumerable<T>() where T : new() {
if (new T() is IEnumerable) {
}
For an ICollection of any type (List or HashSet for example):
internal static bool IsCollection(this Type type) => type.GetGenericArguments().Length > 0 && (typeof(ICollection<>).MakeGenericType(type.GetGenericArguments()[0])).IsAssignableFrom(type);