【问题标题】:Why is this inherited property expression ambiguous to the compiler?为什么这个继承的属性表达式对编译器来说是模棱两可的?
【发布时间】:2020-08-12 03:11:06
【问题描述】:

考虑以下代码:

using System;

namespace Sample
{
    public interface IGetOptions
    {
        string Options { get; }
    }

    public interface ISetOptions
    {
        string Options { set; }
    }

    public interface IOptions : IGetOptions, ISetOptions
    {
    }

    public class MyOptions : IOptions
    {
        public string Options { get; set; }
    }

    public class Program
    {
        public static void Main(string[] args)
        {
            var a = new MyOptions();
            DoIt(a);
            Console.WriteLine(a.Options);
        }

        private static void DoIt(IOptions x)
        {
            x.Options = "test"; // Ambiguity between IGetOptions.Options and ISetOptions.Options
        }
    }
}

我一直将属性理解为返回值或分配值的方法的语法糖。鉴于IGetOptionsISetOptions 之间的冲突只存在于名称上,并且每个接口都定义了getset为什么编译器不能解决上面的歧义?

对我来说,很明显应该在这里调用ISetOptions.set_Options。如果我按如下方式更改代码,一切都会正常编译:

public interface IGetOptions
{
    string Options();
}

public interface ISetOptions
{
    void Options(string value);
}

public interface IOptions : IGetOptions, ISetOptions
{
}

public class MyOptions : IOptions
{
    public string Options() { return _options; }

    public void Options(string value) { _options = value; }

    private string _options;
}

public class Program
{
    public static void Main(string[] args)
    {
        var a = new MyOptions();
        DoIt(a);
        Console.WriteLine(a.Options());
    }

    private static void DoIt(IOptions x)
    {
        x.Options("test");
    }
}

这就是我一直理解的在属性的底层发生的事情,那么为什么编译器不能为我做这个跳转呢?

我也意识到(我认为)一种解决方法是使用new 关键字来隐藏继承的属性。使用我发布的第一个代码 sn-p,对 IOptions 的以下更改使代码编译并按预期工作:

public interface IOptions : IGetOptions, ISetOptions
{
    new string Options { get; set; }
}

这对我来说也很好奇。当然,我从来没有故意使用new 来隐藏继承的属性或方法,但我认为这意味着IOptions.Options 在物理上与IGetOptions.Options 不同,这意味着接受IGetOptions 并评估Options 的方法不会看到如果同一对象也是IOptions,则与IOptions.Options 相同。所以这里的问题是:为什么编译器足够聪明,可以折叠和关联在名称上发生冲突的继承属性,但又不够聪明,无法解决上述歧义?

提前致谢,如果有任何需要澄清的地方,请告诉我。

【问题讨论】:

  • 我明白你的意思,但为什么还要设计这样的东西呢?通常,当您不控制接口并且存在名称冲突时,您会显式地实现一个。即使编译器会处理歧义,您的代码在其他人看来仍然是模棱两可的。
  • 这并不像选择你认为最适合这种情况的那样简单,编译器有一套非常复杂的成员查找规则,你可以在规范中看到它@ 987654338@ 碰巧这种情况属于最后一个要点“否则,查找不明确,并发生绑定时错误。”是的,他们当然可以指定解决这个问题的方法,但是他们没有。可能是这违反了其他规则,或者复杂性超过了好处,或者他们只是不能被打扰,也许@EricLippert 可以跳到这里

标签: c# inheritance interface compiler-construction


【解决方案1】:

C# 语言规范确实描述了“方法组”的概念,允许编译器根据其他标准延迟从一组候选方法中挑选单个方法的过程。

但语言规范目前没有属性组的类似概念。对于nameof(IOptions.Options),您也会遇到此错误,即使无论使用哪个Options 属性,nameof 操作都会产生相同的结果。

为什么编译器不能消除这些属性的歧义?简短的回答是因为它没有。现在,编译器希望将属性名称解析为单个属性,而不关心如何使用该属性的上下文。一个简单、可测试的流程,足以满足开发人员当前的需求。

如果您想为编译器添加支持,您需要记录语言规范将如何更改,以及编译器的各个组件将如何更改。从其他编译器工程师那里获得支持,并让他们相信所有这些额外的复杂性都是必要的。然后实施这些更改并编写单元测试来确认它是如何工作的……而且还没有人这样做。

【讨论】:

    猜你喜欢
    • 2018-06-01
    • 1970-01-01
    • 2018-05-28
    • 2016-03-08
    • 2012-09-29
    • 2013-11-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多