谁能向我解释以下代码为何引发System.Reflection.AmbiguousMatchException? [英] Can anyone explain to me why the following code throws System.Reflection.AmbiguousMatchException?

查看:33
本文介绍了谁能向我解释以下代码为何引发System.Reflection.AmbiguousMatchException?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

using System;
using System.Reflection;

namespace A
{
  interface IObjectWithId<TId>
  {
    TId Id { get; }
  }
  interface IEntityBase : IObjectWithId<object>
  {
    new object Id { get; }
  }
  abstract class BusinessObject<TId> : IObjectWithId<TId>
  {
    public abstract TId Id { get; }
  }
  class EntityBase : BusinessObject<object>, IEntityBase
  {
    public override object Id { get { return null; } }
  }

  public static class Program
  {
    public static void Main()
    {
      Console.WriteLine(typeof(EntityBase).GetProperty("Id", BindingFlags.Instance | BindingFlags.Public));
    }
  }
}

我得到这个:

System.Reflection.AmbiguousMatchException was unhandled
  Message="Ambiguous match found."
  Source="mscorlib"
  StackTrace:
       at System.RuntimeType.GetPropertyImpl(String name, BindingFlags bindingAttr, Binder binder, Type returnType, Type[] types, ParameterModifier[] modifiers)
       at System.Type.GetProperty(String name, BindingFlags bindingAttr)
       at A.Program.Main() in C:\Home\work\A\Program.cs:line 26
  InnerException: 

Microsoft Visual Studio 2008
版本9.0.30729.1 SP
Microsoft .NET Framework
版本3.5 SP1

Microsoft Visual Studio 2008
Version 9.0.30729.1 SP
Microsoft .NET Framework
Version 3.5 SP1

奇怪的是,看起来其他人无法复制它.虽然它确实每次都会在我的机器上崩溃.我发现这段代码:

Strangely enough, looks like other folks fail to reproduce it. Though it does crash on my machine every time. I found out that this code:

Console.WriteLine(typeof(EntityBase).GetProperty("Id", BindingFlags.Instance | BindingFlags.Public, null, typeof(object), Type.EmptyTypes, null));

可以正常工作,尽管应该相同.

Does work OK, although it should be the same.

推荐答案

为回答您的问题,我将使您熟悉术语方法表".这是.NET框架中类型的内部表示的一部分,其中每个.NET类型都有自己的方法表.您可以将其想象为包含该类型的所有方法和属性的哈希图(或字典).关键是方法/属性签名(方法名称和参数类型,没有返回类型),值是匹配的方法/属性的集合,以及一些反射元数据信息,例如哪种类型声明了方法/属性.

To get your question answered, I'll get you familiar with the term 'Method Table'. This is part of the internal representation of types in the .NET framework, where every .NET type has its own method table. You can imagine it as a hash map (or a dictionary) containing all the methods and properties of the type. The key is the method/property signature (method name and parameter types, without the return type) and the value is a collection of matching methods/properties, alongside some reflection metadata information such as which type has declared the method/property.

当类 A 从基类- B 派生,或实现接口 C 时,方法表中的项目> B C A 的方法表中直接可用.如果 A 的方法表中已经包含具有特定签名的项目,则该项目将添加到具有相同签名的集合中,因此现在 A 将具有2个方法/属性签名指向.区分这些重复条目的唯一方法是比较描述声明签名所代表的类型的元数据.

When class A derives from a base class - B, or implements an interface C, the items in the method table of B and C become directly available in the method table of A. If the method table of A already contains an item with certain signature, that item is added to the collection for the same signature, so now A will have 2 methods/properties that the signature points to. The only way to distinguish these duplicate entries is by comparing the metadata describing the type on which behalf the signature is declared.

让我们采用接口 IObjectWithId< TId> ,该接口定义了属性 TId ID {get;放;} . EntityBase 类实现 IObjectWithId< TId> ,因此接收到 TId ID {get;放;} 属性添加到其方法表.同时,此类实现了 IEntityBase 接口,该接口为其提供了 Object ID {get;放;} 属性.然后, EntityBase 类将在同一签名下接收两个属性(因为返回类型不参与签名),而它仍将公开2个不同的属性.以下声明将导致编译错误:

Lets take the interface IObjectWithId<TId>, which defines a property TId ID { get; set; }. The EntityBase class implements IObjectWithId<TId> so receives an TId ID { get; set; } property to its method table. At the same time this class implements the IEntityBase interface, which gives it the Object ID { get; set; } property. The EntityBase class then receives two properties under the same signature (because the return type does not participate in the signature), while it will still expose 2 different properties. The following declaration will result in a compile error:

   public class EntityBase : IEntityBase, IObjectWithId<int>
   {
        public int ID { get; set; }
   }

因为未实现 IEntityBase .同样,跟随也会失败:

because the IEntityBase is not implemented. Similarly, the follwing will also fail:

   public class EntityBase : IEntityBase, IObjectWithId<int>
   {
        public object ID { get; set; }
   }

因为这次不满足 IObjectWithId< int> .您可以尝试这样做:

because this time IObjectWithId<int> is not satisfied. You may try to do this:

   public class EntityBase : IEntityBase, IObjectWithId<int>
   {
        public object ID { get; set; }
        public int ID { get; set; }
   }

仅仅因为具有两个具有相同签名的属性而收到另一个编译错误.

just to receive another compilation error for having 2 properties with the same signature.

解决此问题的方法是显式实现至少一个冲突的签名:

The way to work this around, is to implement at least one of the conflicting signatures explicitly:

   public class EntityBase : IEntityBase, IObjectWithId<int>
   {
        private object objID;
        private int intID;

        object IEntityBase.ID { get { return objID; } set { objID = value; } }     
        int IObjectWithId<int>.ID { get { return intID; } set { intID = value; } }
   }

现在,回到您的代码-您使用了 object 而不是 TId ,这创建了一种罕见但有趣的情况-两个 ID 属性统一,因为它们具有相同的签名.所以这个课:

Now, back to your code - you used object instead of TId which creates a rare but interesting case - the two ID properties unify because of their identical signature. So this class:

   public class EntityBase : IEntityBase, IObjectWithId<object>
   {
        public object ID { get; set; }
   }

将编译,因为 ID 属性同时满足两个接口.但是, EntityBase 类在其方法表中仍然具有两个 ID 属性(每个接口都有一个属性).编译器会在 EntityBase 类中将这两个属性自动分配给同一实现(该过程称为 unification ).

will compile, because the ID property satisfies both interfaces. However, the EntityBase class still has two ID properties in its method table (one coming form each interface). The two properties are automatically assigned to the same implementation in the EntityBase class by the compiler (the process is called unification).

以下代码:

typeof(EntityBase).GetProperty(
    "ID", BindingFlags.Instance | BindingFlags.Public);

将查看 EntityBase 类的方法表,并将看到该签名的两个属性条目,并且不知道选择哪个.

will look into the method table of the EntityBase class and will see two property entries for that signature and will not know which one to pick.

这是因为您可能已经实现了这样的类:

This is because you might have implemented your class like that:

   public class EntityBase : IEntityBase, IObjectWithId<object>
   {
        private object objID1;
        private int objID2;

        object IEntityBase.ID 
        { 
            get { return objID1; } 
            set { objID1 = value; } 
        }

        object IObjectWithId<object>.ID 
        {
            get { return objID2; } 
            set { objID2 = value; } 
        }
   }

请参阅-这两个属性可以具有不同的实现,并且那时运行时无法知道它们的实现是否统一(反射发生在运行时上,而不是在编译时发生进行统一的时间).您收到的 AmbiguousMatchException 是.NET框架阻止您执行可能具有未知/意外行为的代码的方法.

See - the two properties can have different implementations, and at that point the runtime cannot know whether their implementations are unified (reflection happens at runtime now, not at compile time when unification was performed). The AmbiguousMatchException you received is the .NET framework's way to prevent you from executing code with possibly unknown/unintended behavior.

当没有为每个接口提供不同的实现时(如您的情况),该签名的方法表中的两个条目都将调用您唯一的实现,但仍然有两个条目指向相同的属性.为避免框架混乱,您应该在继承层次结构中使用 high 类型,这样它的方法表中只有一个要反映成员的条目.在我们的示例中,如果在反映 Id 属性时改用 interface 类型,则将解决情况,因为每个接口的方法表中只有一个条目要求的签名.

When no different implementation is provided for each interface (as in your case), the only implementation you have is called by both entries in the method table for that signature, but still there are two entries pointing to the same property. To prevent the framework from confusion, you should use a type high enough in the inheritance hierarchy, so that it has only one entry in its method table for the member you want to reflect. In our example, if we use the interface types instead when reflecting the Id property, we will solve our case, as each of the interfaces has only one entry in its method table for the requested signature.

然后您可以使用

Console.WriteLine(
    typeof(IEntityBase).GetProperty(
        "Id", BindingFlags.Instance | BindingFlags.Public));

Console.WriteLine(
    typeof(BusinessObject<object>).GetProperty(
        "Id", BindingFlags.Instance | BindingFlags.Public));

取决于要检索的实现.在我的最新示例中,每个接口都有不同的实现,您可以通过选择正确的接口来调用反射的任何实现.在问题的示例中,您可以使用所需的任何接口,因为两者都有一个实现.

depending which implementation you want to retrieve. In case of my latest example, where each interface has different implementation, you have the ability to invoke reflective any of the implementations, by choosing the correct interface. In the example from your question, you may use whichever interface you want, as both have one implementation.

这篇关于谁能向我解释以下代码为何引发System.Reflection.AmbiguousMatchException?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆