简体   繁体   中英

Determine whether a C# method has keyword 'override' using Reflection

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.

You can try this

public static bool IsOverriding(this MethodInfo methodInfo)
{
    if (methodInfo == null) throw new ArgumentNullException();
    return methodInfo.GetBaseDefinition() != methodInfo;
}

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM