Как узнать, является ли MemberInfo явной реализацией свойства

Представьте, что у меня есть код ниже. Как я могу получить отражение MemberInfo/PropertyInfo для "явной" реализации Test.Name?

Кроме того, есть ли какой-либо способ программно знать, что MemberInfo является явной реализацией свойства интерфейса?

public interface ITest
{
    string Title { get; set; }
}

public interface IExplicit
{
    string Name { get; set; }
}

public class Test : ITest,IExplicit
{

    public string Title { get; set; }

    string IExplict.Name
    {
        get
        {
            return this.Title;

        }
        set
        {

        }
    }
}

Ответы

Ответ 1

Представьте, что у вас есть этот интерфейс:

interface ITest
{
    bool MyProperty { get; set; }
}

Реализовано в этом классе:

class Test : ITest
{
    bool ITest.MyProperty { get; set; }
}

Теперь добавьте это свойство в Test (обратите внимание, что они имеют одинаковое имя):

public bool MyProperty { get; set; }

С простым GetProperties() вы не получите явную реализацию интерфейса (потому что это всегда частный член):

int count = new Test().GetType().GetProperties().Length; // It 1!

Если вы включаете в себя как членов Public, так и NonPublic, вы получите оба. Чтобы отличить их, вы можете положиться сначала на имя: явная реализация будет содержать полное имя интерфейса (так что вы можете искать ., он не будет там для нормального свойства, потому что это не разрешенный char):

public static bool IsExplicitInterfaceImplementation(PropertyInfo property)
{
    return property.Name.Contains(".");
}

Это немного наивно, поэтому вам может понадобиться дополнительная проверка, вы можете утверждать, что метод get этого свойства будет:

  • Есть virtual и sealed.
  • Есть private.
  • Содержит хотя бы одну точку.
  • Не запускается с get_ или _set

Изменить код:

public static bool IsExplicitInterfaceImplementation(PropertyInfo property)
{
    // This check is not mandatory and not cross-languages.
    // How this method is named may vary
    if (!property.Name.Contains("."))
        return false;

    if (property.Name.StartsWith("get_"))
        return false;

    if (!property.GetMethod.IsFinal)
        return false;

    if (!property.GetMethod.IsVirtual)
        return false;

    if (!property.GetMethod.IsPrivate)
        return false;

    return true;
}

Конечно, не все эти проверки необходимы, я думаю, что первых двух достаточно, чтобы исключить большую часть кода, сгенерированного компилятором.

Если вы знаете, какой интерфейс может быть явно реализован, вы найдете здесь этот вопрос на SO довольно полезным: Как найти, реализует ли метод определенный интерфейс

ИЗМЕНИТЬ
Из комментариев, которые я подумал об этом, и я обнаружил, что нет правильного способа сделать это, CLR не применяет какое-либо правило (AFAIK), потому что необходимо только соединение между методом интерфейса и методом класса (как бы оно ни называлось). Я полагаю (но он может быть расслабленным или расширенным для других языков, если кто-то внесет вклад с большим количеством тестов, я сделаю этот ответ вики), этот код может работать в большинстве случаев (спасибо Alxandr за подсказку):

Первая общая функция для проверки того, является ли метод (с учетом MethodInfo) явной реализацией интерфейса или нет.

То, что мы не можем утверждать:

  • Мы не можем использовать имя (для проверки, например, "." ), потому что он зависит от реализации (С# использует имя интерфейсаName.methodName, а на других языках нет).

  • Мы не можем полагаться на проверку для private, потому что (например) в С++/CLI это может быть общедоступный метод (с другим именем), кроме того, интерфейс может быть "взломан", чтобы быть внутренним, но разработчик быть общедоступным (поэтому метод также не будет публичным).

Что мы можем утверждать:

  • Явная реализация интерфейса всегда запечатана и виртуальна. Возможно, это "неверно" для всех языков, поэтому мы можем ослабить это правило.

  • Если метод не имеет то же имя метода, объявленного в интерфейсе, он реализует тогда явную реализацию.

Это код:

public static bool IsExplicitInterfaceImplementation(MethodInfo method)
{
    // Check all interfaces implemented in the type that declares
    // the method we want to check, with this we'll exclude all methods
    // that don't implement an interface method
    var declaringType = method.DeclaringType;
    foreach (var implementedInterface in declaringType.GetInterfaces())
    {
        var mapping = declaringType.GetInterfaceMap(implementedInterface);

        // If interface isn't implemented in the type that owns
        // this method then we can ignore it (for sure it not
        // an explicit implementation)
        if (mapping.TargetType != declaringType)
            continue;

        // Is this method the implementation of this interface?
        int methodIndex = Array.IndexOf(mapping.TargetMethods, method);
        if (methodIndex == -1)
            continue;

        // Is it true for any language? Can we just skip this check?
        if (!method.IsFinal || !method.IsVirtual)
            return false;

        // It not required in all languages to implement every method
        // in the interface (if the type is abstract)
        string methodName = "";
        if (mapping.InterfaceMethods[methodIndex] != null)
            methodName = mapping.InterfaceMethods[methodIndex].Name;

        // If names don't match then it explicit
        if (!method.Name.Equals(methodName, StringComparison.Ordinal))
            return true;
    }

    return false;
}

С помощью этой вспомогательной функции для проверки свойств:

public static bool IsExplicitInterfaceImplementation(PropertyInfo property)
{
    // At least one accessor must exists, I arbitrary check first for
    // "get" one. Note that in Managed C++ (not C++ CLI) these methods
    // are logically separated so they may follow different rules (one of them
    // is explicit and the other one is not). It a pretty corner case
    // so we may just ignore it.
    if (property.GetMethod != null)
        return IsExplicitInterfaceImplementation(property.GetMethod);

    return IsExplicitInterfaceImplementation(property.SetMethod);
}

Ответ 2

Используйте флаги привязки BindingFlags.Instance | BindingFlags.NonPublic, чтобы получить элементы, явно объявленные. Также вам нужно указать полное имя для явных членов, если вы хотите получить их по имени:

Type type = typeof(Test);
var flags = BindingFlags.Instance | BindingFlags.NonPublic;
var property = type.GetProperty("Namespace.IExplicit.Name", flags);

Этот факт можно использовать для фильтрации только явно реализованных memebers:

var explicitMembers = type.GetMembers(flags).Where(m => m.Name.Contains("."));

Ответ 3

Если оба интерфейса и классы находятся в одном и том же пространстве имен, Вы можете попробовать следующее:

var interfaces = (typeof(Test)).GetInterfaces();
var p = typeof(Test).GetProperties(
    BindingFlags.Instance |
    BindingFlags.NonPublic);
var result = interfaces.SelectMany(i => i.GetMembers())
        .Select(m =>
        {
            var name = m.DeclaringType.FullName +"."+ m.Name;
            Console.WriteLine(name);
            return name;
        })
        .Intersect(p.Select(m =>
        {
            Console.WriteLine(m.Name);
            return m.Name;
        }))
        .ToList();

Ответ 4

Чтобы получить PropertyInfo для любой существующей реализации (неявной или явной) конкретного свойства интерфейса, используйте следующий код:

var pInfoName = typeof(IExplicit).GetProperty("Name");
//...
Test tObj = new Test() { Title = "Test" };
string explicitName = (string)pInfoName.GetValue(tObj, new object[] { });

В классифицировать существующая PropertyInfo использует подходы, показанные в ответе @Adriano.