16

想象一下我有下面的代码。我怎样才能通过反射得到MemberInfo/PropertyInfo'显式'实现Test.Name

另外,有没有办法以编程方式知道 aMemberInfo是接口属性的显式实现?

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

4 回答 4

16

想象一下你有这个界面:

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成员,您将同时获得两者。为了区分它们,您可以首先依赖名称:显式实现将包含完整的接口名称(因此您可以查找 a .,它不会出现在普通属性中,因为它不是允许的字符):

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

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

如果您知道可以显式实现哪个接口,您会发现这个问题非常有用:如何查找方法是否正在实现特定接口

编辑
从评论中我想到了这一点,我发现没有合适的方法来做到这一点,CLR 不应用任何规则(AFAIK),因为所需要的只是接口方法和类方法之间的链接(不管它是如何调用的)。我想(但对于其他语言可能会放宽或扩展,如果有人愿意提供更多测试,我会将这个答案作为 wiki)此代码在大多数情况下可能有效(感谢 Alxandr 的提示):

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

我们不能断言:

  • 我们不能使用名称(检查,例如“.”),因为它依赖于实现(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);
}
于 2013-07-25T09:27:24.843 回答
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("."));
于 2013-07-25T09:22:15.593 回答
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();
于 2013-07-25T09:32:31.340 回答
-1

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

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

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

于 2013-07-25T09:35:23.980 回答