C# 为什么用相同的属性实现多个接口显示';歧义';警告

C# 为什么用相同的属性实现多个接口显示';歧义';警告,c#,interface,compiler-errors,C#,Interface,Compiler Errors,相关职位: 来自同一来源的代码: private interface IBase1 { int Percentage { get; set; } } private interface IBase2 { int Percentage { get; set; } } private interface IAllYourBase : IBase1, IBase2 { } privat

相关职位:

来自同一来源的代码:

    private interface IBase1
    {
        int Percentage { get; set; }
    }

    private interface IBase2
    {
        int Percentage { get; set; }
    }

    private interface IAllYourBase : IBase1, IBase2
    {
    }

    private class AllYourBase : IAllYourBase
    {
        private int _percentage;

        public int Percentage
        {
            get { return _percentage; }
            set { _percentage = value; }
        }
    }

    private void Foo()
    {
        IAllYourBase iayb = new AllYourBase();
        int percentage = iayb.Percentage; // Fails to compile. Ambiguity between 'Percentage' property. 
    } 
(但没有回答我的问题——“为什么合同变得模棱两可?”)

鉴于:

接口是实现类必须遵守的契约

如果两个(或多个)接口要求相同的契约,并且一个接口将它们“向前”传递,然后类实现了这两个契约,并接受公共契约应仅作为实现类的一个契约(通过不提供显式实现)。那么

  • 为什么编译器在常见契约上显示“歧义”警告

  • 为什么编译器在试图通过接口(iayb.Percentage)访问不明确的约定时无法编译

  • 我想知道编译器使用此限制有什么好处

    编辑:提供一个真实世界的用例,我希望将跨接口的契约作为一个契约使用

    public interface IIndexPriceTable{
          int TradeId{get;}
          int IndexId{get;}
          double Price{get;}
    }
    
    public interface ILegPositionTable{
          int TradeId {get;}
          int LegId {get;}
          int Position {get;}
    }
    
    public interface ITradeTable {
          int TradeId{get;}
          int IndexId{get;}
          int LegId{get;}
          //others
    }
    
    public interface IJoinedTableRecord : IIndexPriceTable, ILegPositionTable, ITradeTable {
         //Just to put all contracts under one interface and use it as one concrete record, having all information across different tables.
    }
    
    • 为什么我希望在我的联接表记录中有3-TradeId、2-LegId和2-IndexId

    因为编译器无法确定您试图访问哪个基本接口实现(
    IBase1.Percentage
    IBase2.Percentage
    ),因为您的
    iallyurbase
    接口紧跟这两个接口,并且它们各自都有自己的
    Percentage
    属性


    这样说:仅仅因为两个接口具有相同名称和类型的属性,并不意味着该属性在两个接口中的工作方式相同。即使公共接口继承自两个具有相同成员的接口,编译器也不能将两个看似相同的属性合并为一个,因为它们是两个不同契约的成员。

    我明白你的意思。我想这个编译器限制的主要好处是最好有一个,而不是没有。也就是说,有更多的伤害,那么你无意中的界面混乱将被忽略,然后受益(如果有任何)从这个奇怪的情况下,你想要这样的行为


    顺便说一句,在任何现实场景中,所需的行为都会非常有用?

    因为接口
    iallyurbase
    没有声明
    百分比
    属性本身

    当您将
    AllYourBase
    的实例分配给
    iallyurbase
    变量时,编译器需要输出对
    IBase1.Percentage
    IBase2.Percentage
    的调用:

    callvirt   instance int32 IBase1::get_Percentage()
    

    它们是不同类型的不同成员,仅仅因为它们具有相同的签名并不意味着它们可以互换


    在现实世界中,您可能需要更细粒度的接口来定义公共属性。

    如果一个接口继承了另外两个具有相似命名成员的接口,则必须应用以下两个条件之一:

  • 两个接口都从其他接口继承同一成员。另一个接口必须是公共的,但可以记录它的存在纯粹是为了继承,并且使用者不需要声明其类型的变量或参数。
  • 继承其他接口的接口将自己的同名成员声明为“new”。当一个接口声明只读属性而另一个接口声明同名的只读属性时,这是一种很好的方法;组合这两个接口的接口可以声明一个读写属性,该属性的实现将使用只读属性的“getter”和只读属性的“setter”。不过,我不确定这在其他很多情况下是否会有好处。 如果不做这些事情,编译器最好不要尝试猜测。假设有一个接口
    IListOfDigits
    ,它的
    Add
    方法将一个整数0-9追加到列表中,还有
    ibignNumber
    ,它的
    Add
    方法将一个数字进行算术相加。还有一个接口IListoDigits代表BigNumber,它继承了这两者。给定一个名为
    myThing
    的代表BigNumber的
    IListoDigits,其中包含数字“5,4,3,2”,那么myThing的效果应该是什么。添加(1)?它是否应该将
    myThing
    更改为保持“5,4,3,2,1”(添加
    的数字的效果)或“5,4,3,3”(添加
    的IBignNumber.Add的效果)?如果执行上述任一操作,编译器将毫不费力地确定要使用哪个
    Add
    方法。否则,如果两个方法都可以接受
    int
    ,那么它就没有线索了


    顺便说一句,泛型和重载构成了一个有趣的例子。如果
    IFoo
    具有成员
    void Bar(T参数)
    void Bar(U参数)
    ,则不能将类声明为实现
    IFoo
    。另一方面,可以将一个类
    Foo
    声明为实现
    IFoo
    ,然后将另一个类声明为继承自
    Foo
    ,因为即使
    T
    U
    引用相同的类型,编译器仍将使用
    T
    U

    解决重载问题。解决方案是使用如下新关键字再次定义属性百分比:

    private interface IBase1
    {
        int Percentage { get; set; }
    }
    
    private interface IBase2
    {
        int Percentage { get; set; }
    }
    
    private interface IAllYourBase : IBase1, IBase2
    {
       new int Percentage { get; set; }
    }
    
    private class AllYourBase : IAllYourBase
    {
        private int _percentage;
    
        public int Percentage
        {
            get { return _percentage; }
            set { _percentage = value; }
        }
    }
    
    private void Foo()
    {
        IAllYourBase iayb = new AllYourBase();
        int percentage = iayb.Percentage; //OK
    } 
    
    注意:


    对接口的C#方法与对接口的C#方法非常不同。在C#中,您必须声明,类通过修改类本身来实现接口,而在C++14中,它只需要具有与接口定义相对应的方法。因此,C++中的代码比C++14中的代码具有更多的依赖关系。

    int percentage=iayb.percentage
    不知道它在处理
    AllYourBase
    类,只是不管它是什么,它实现了
    iallyurbase
    接口

    因此,假设我尝试使用我的
    DoubleBase
    类执行相同的语句:

    private class DoubleBase : IAllYourBase
    {
        int IBase1.Percentage { get; set; } = 10;
    
        int IBase2.Percentage { get; set; } = 20;
    }
    
    int percentage
    设置为什么值?

    但作为
    private class DoubleBase : IAllYourBase
    {
        int IBase1.Percentage { get; set; } = 10;
    
        int IBase2.Percentage { get; set; } = 20;
    }