Determine whether a C# method has keyword 'override' using Reflection
I had expected to find an answer easily to this problem, but I didn'开发者_Go百科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 containabstract
orvirtual
modifier.new
is used to hide based method with the same name but...new
cannot be apply toabstract
base method because it generate compiler error.- However the interesting part is
new
also can be applied to method if the base method containvirtual
.
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
orout
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;
}
精彩评论