Я тоже пытаюсь найти эту вещь. Из вопроса вы даете идею получить 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.