Определите, есть ли у метода C # ключевое слово override, используя Reflection - PullRequest
3 голосов
/ 21 апреля 2011

Я ожидал легко найти ответ на эту проблему, но не смог. Я хотел бы знать, возможно ли определить, имеет ли метод ключевое слово 'override', связанное с ним, учитывая его экземпляр MethodInfo.

Я думал, что может быть достигнуто следующее:

/// <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;
}

Я успешно протестировал некоторые не виртуальные, виртуальные и абстрактные примеры, но я чувствую, что мне не хватает некоторых сценариев, может быть, с сокрытием или обобщениями (хотя я не могу понять, как это вступит в игру).

Ответы [ 3 ]

2 голосов
/ 06 июля 2011

Я тоже пытаюсь найти эту вещь. Из вопроса вы даете идею получить IsOverriding работу для override ключевого слова. Однако для сокрытия я пытаюсь создать IsHiding для ключевого слова new. Вот основной C # ООП:

  • override используется только тогда, когда основанный метод содержит модификатор abstract или virtual.
  • new используется для скрытия основанного метода с тем же именем, но ...
  • new нельзя применить к abstract базовому методу, поскольку он генерирует ошибку компилятора.
  • Однако интересная часть new также может быть применена к методу, если базовый метод содержит virtual.

Интересная часть - мы можем скрыть или переопределить метод virtual. Мы знаем, что GetBaseDefinition() вернет базовый MethodInfo, если мы override a virtual метод. но ключ к его различению - GetBaseDefinition() вернет тот же MethodInfo вместо базового MethodInfo, если мы скрываем метод virtual.

override ключевое слово является обязательным, в то время как new используется только для подавления предупреждающего сообщения. Таким образом, мы можем дифференцировать override и new путем объединения IsAbstract и IsVirtual с DeclaringType и 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;
    }

Я проверяю его, используя простое наследование, и оно работает. Я не думаю о родовом методе .. пока ..


EDIT : Я просто изменил код выше, потому что я забыл, что идея о унаследованном типе может содержать недавно объявленный метод, который не должен рассматриваться как новый. IsHiding() сначала удостоверится, что он имеет тот же DeclaringType (кажется, что он новый), но нужно взглянуть на базовые типы объявления на DeclaringType.BaseType, если существует метод с тем же именем.

Обратите внимание, что из-за отсутствия BindingFlags.DeclaredOnly, GetMethod() будет выполнять поиск по всем базовым типам, поэтому нет необходимости рекурсивного поиска по каждому базовому типу. BindingFlags.FlattenHierarchy используется для включения статического метода в абстрактный-абстрактный базовый класс, например:

public abstract class A
{
    public static void Stat() { }
}
public abstract class B : A
{
}
public class C: B
{
    public new static void Stat() { }
}

EDIT : Я просто исправляю IsHiding() выше, чтобы проверить базовый метод перегрузки и предотвратить AmbiguousMatchException, используя GetMethods() вместо GetMethod(). Перегрузки, проверенные на работу с комбинацией с различными параметрами, смешиваются с ref, out, params и дополнительным параметром. Подпись для сравниваемых перегрузок на основе количества параметров, типов параметров и их модификатора:

  • ref или out включены в подпись, но оба не могут быть перегружены друг на друга.
  • тип возвращаемого значения, необязательный (значение по умолчанию) и params для самого правого параметра следует игнорировать

ref сравнивается самим ParameterType (см. Окончание '&' во время отладки) и нет необходимости сравнивать по IsByRef, тогда как out сравнивается с помощью флага Attributes. Я использую упрощенное выражение для побитового XOR, чтобы пропустить цикл, если и только если один из атрибутов имеет флаг Out, который отличает сигнатуру. Не путайте с HasFlag в .NET 4, просто нужно убедиться, что Out бит равен 1 по результату XOR.

0 голосов
/ 21 апреля 2011

Ну, я не вижу, как это вступит в игру .Ваш код действительно определяет, определен ли метод или переопределен.

В случае скрытия тип объявления - это тот, который скрывает метод через new.В случае обобщенных типов все методы определяются классом шаблона.

0 голосов
/ 21 апреля 2011

Вы можете попробовать это

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