【问题标题】:Properly finding an inherited interface property through reflection通过反射正确找到继承的接口属性
【发布时间】:2017-02-07 16:26:38
【问题描述】:

这是一个由两部分组成的问题。首先,哪些显式属性实现绑定到IAllTogether.SomeInt,为什么编译器不抱怨歧义?当您注释掉标记的行时会这样做。

public interface IFirst
{
    int SomeInt { get; }
}

public interface ISecond
{
    int SomeInt { get; }
}

public interface ICombined : IFirst, ISecond
{
    new int SomeInt { get; } // Comment this line.
}

public interface IAllTogether : IFirst, ISecond, ICombined
{ }

public sealed class Implementation : IAllTogether
{
    int ICombined.SomeInt { get { return 0; } } // Comment this line.

    int IFirst.SomeInt { get { return 0; } }

    int ISecond.SomeInt { get { return 0; } }
}


IAllTogether t = new Implementation();
var unimportant = t.SomeInt;

第二个问题是:当给定接口Type 和属性名称时,如何找到正确的PropertyInfo?我可以使用GetInterfaces()GetProperty() 列出所有可能的候选人,但我怎么知道哪个是正确的?我试过typeof(IAllTogether).GetProperty("SomeInt"),但它不起作用。

编辑

看起来第一部分的答案是隐藏继承的成员解决了歧义。然而,关于第二部分甚至没有一条评论:如何可靠地为某些属性名称和接口类型找到正确的PropertyInfo

编辑 2

澄清问题的第二部分。我正在寻找的是一种为任何未知的Type 获取正确属性的方法。基本上是这样的方法:

public static PropertyInfo GetPropertyOfInterface(Type interfaceType, string propertyName)
{
    if (!interfaceType.IsInterface)
        throw new ArgumentException();

    // for interfaceType == typeof(IAllTogether), return ICombined.SomeInt
    // for interfaceType == typeof(IFirst), return IFirst.SomeInt
}

【问题讨论】:

  • 如果我运行该代码,我可以看到来自ICombined 的那个被调用,可能是因为new 隐藏了另外两个的关键字。顺便说一句:ReSharper 告诉我,在 IAllTogether 的声明中明确声明 IFirstISecond 是多余的,因为它们无论如何都是通过 ICombined 继承的。
  • 因为 IFirstISecond 中的 SomeInt 现在被 ICombined 中的 new SomeInt 隐藏,因此将使用 ICombined.SomeIntICombined.SomeInt 的优先级高于其他两个不是因为 new 关键字。但是因为ICombined 实现了另外两个接口。因此它可以隐藏他们的成员。
  • 如果我找到第二部分的答案,我会告诉你。
  • 您能否为第二个问题指定或举例说明?我在答案中添加了一行。 什么值你想要获得一个属性?正确的属性是typeof(ICombined).GetProperty("SomeInt")。如果注释行被删除,则取决于您实际调用的内容。
  • @RenéVogt 想象一下,您正在编写一个采用 Type 参数和字符串参数的方法:接口类型和参数名称。该方法需要返回正确的 PropertyInfo 对象。你对 IAllTogether 或 ICombined 一无所知。我将编辑答案以澄清。

标签: c# reflection properties interface language-lawyer


【解决方案1】:

很多人回答第一部分:如果一个接口隐藏了原始接口的成员,则不考虑。

使用这些信息,这是我在第二部分的尝试。欢迎对问题或改进提出意见。

public static PropertyInfo GetPropertyOfInterface(Type interfaceType, string propertyName)
{
    if (interfaceType == null)
        throw new ArgumentNullException("interfaceType");

    if (!interfaceType.IsInterface)
        throw new ArgumentException(
            string.Format("Type {0} doesn't represent an interface.",
                interfaceType.FullName),
            "interfaceType");

    // If declared in given interface, just return that.
    var declaredProperty = interfaceType.GetProperty(propertyName);
    if (declaredProperty != null)
        return declaredProperty;

    // Otherwise, first finding all the candidates.
    var candidates = new HashSet<PropertyInfo>(
        interfaceType.GetInterfaces().Select(t => t.GetProperty(propertyName)));
    candidates.Remove(null);

    if (candidates.Count == 0)
        throw new ArgumentException(
            string.Format("Property {0} not found in interface {1}.",
                propertyName, interfaceType.FullName),
            "propertyName");

    // Finally, removing all candidates the first candidates hide.
    var originalCandidates = candidates.ToList();
    candidates.ExceptWith(
        originalCandidates.SelectMany(prop => prop.DeclaringType.GetInterfaces())
                          .Select(t => t.GetProperty(propertyName)));

    if (candidates.Count != 1)
        throw new AmbiguousMatchException(
            string.Format("Property {0} is ambiguous in interface {1}.",
                propertyName, interfaceType.FullName));

    return candidates.First();
}

【讨论】:

  • 不错。您是否使用继承模式的不同变体对其进行了测试?
  • @RenéVogt 除了给定的课程外,没有太多时间来测试它。所以不,不幸的是。我今天晚些时候可能会这样做。
【解决方案2】:

回答你的第一个问题。

编译器只需要知道接口成员是否被实现。由运行时决定调用哪个版本的成员。

在您的示例中,您使用的是显式实现。

在这种情况下,首先编译检查是否实现了SomeInt 属性。如果这是隐式实现的,它不会抱怨。如果任何一个接口属性被显式实现,编译器将检查其余接口属性是通过隐式实现还是显式实现。

【讨论】:

  • 对不起,我不明白。运行时如何决定为通过 IAllTogether 类型的引用访问的 Implementation 类型的对象调用这些显式实现中的哪一个?
  • 它将使用ICombined 的版本,因为它的接口成员隐藏了它的父接口的成员,而且即使它没有隐藏父成员,它也是 IAllTogether 的唯一直接父级。因此,常见的继承规则就是查找 self 成员,如果找到则调用它,否则向上查找并尝试在直接父级中查找,如果找到则调用它,否则向上查找,依此类推。
猜你喜欢
  • 1970-01-01
  • 2011-03-19
  • 1970-01-01
  • 2013-05-24
  • 1970-01-01
  • 2011-01-09
  • 1970-01-01
  • 2016-03-27
  • 1970-01-01
相关资源
最近更新 更多