如何知道MemberInfo是否是属性的显式实现

时间:2013-07-25 09:09:26

标签: c# .net reflection

想象一下,我有下面的代码。如何通过反映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
        {

        }
    }
}

4 个答案:

答案 0 :(得分:12)

想象一下,你有这个界面:

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

在此课程中实施:

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

现在让我们将此属性添加到Test(请注意,它们具有相同的名称):

public bool MyProperty { get; set; }

使用 plain GetProperties(),您将无法获得显式接口实现(因为它始终是私有成员):

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

如果您同时包含PublicNonPublic成员,则可以同时获得这两个成员。为了区分它们,您可以首先依赖名称:显式实现将包含完整的接口名称(因此您可以查找.,它不会出现普通属性,因为它不是一个允许的字符):

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

这有点天真,所以你可能想要一些额外的检查,你可以断言该属性的get方法将:

  • virtualsealed
  • 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;
}

当然不是所有这些检查都是必需的,我认为前两个足以排除大多数编译器生成的代码。

如果您知道可以明确实施哪个界面,那么您可以在此处找到此问题非常有用:How to find if a method is implementing specific interface

修改
从评论我想到这一点,我发现没有一个正确的方法来做到这一点,CLR不应用任何规则(AFAIK),因为所需要的只是接口方法和类方法之间的链接(无论如何调用)。我想(但是对于其他语言可能会放宽或扩展,如果有人会通过更多测试做出贡献,我会将此答案作为维基)这段代码可能适用于大多数情况下(感谢Alxandr的提示):

检查方法(给定MethodInfo)是否是显式接口实现的第一个通用函数。

我们无法断言:

  • 我们不能使用name(检查,例如“。”)因为它依赖于实现(C#使用interfaceName.methodName,但其他语言没有)。

  • 我们不能依赖于私有检查,因为(例如)在C ++ / 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's 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's 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's 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's a pretty corner case
    // so we may just ignore it.
    if (property.GetMethod != null)
        return IsExplicitInterfaceImplementation(property.GetMethod);

    return IsExplicitInterfaceImplementation(property.SetMethod);
}

答案 1 :(得分:2)

使用BindingFlags.Instance | BindingFlags.NonPublic绑定标志来获取显式声明的成员。如果您想通过名称获取它们,还需要为显式成员指定全名:

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

您可以使用此事实仅过滤明确实施的成员:

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

答案 2 :(得分:1)

如果您的接口和类都位于同一名称空间中,您可以试试这个:

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();

答案 3 :(得分:-1)

要获取特定接口属性的任何现有实现(impicit或explicit)的PropertyInfo,请使用以下代码:

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

分类现有PropertyInfo使用@Adriano答案中显示的方法。