sin*_*law 24 c# compiler-errors
在以下代码中:
interface IGet { int Value { get; } }
interface ISet { int Value { set; } }
interface IBoth : IGet, ISet { }
class Test
{
public void Bla(IBoth a)
{
var x = a.Value; // Error: Ambiguity between 'IGet.Value' and 'ISet.Value'
}
}
Run Code Online (Sandbox Code Playgroud)
我收到一个错误:
'IGet.Value'和'ISet.Value'之间的歧义
为什么编译器无法确定访问的属性必须来自IGet?
编辑 - 我猜测编译器首先尝试确定正在访问哪个属性,然后才检查它是get还是set.问题是 - 为什么?为什么不排除那些不提供吸气剂的候选人呢?为什么不解析实际的get/set方法并忽略它是属性的事实?
更新:使用方法而不是属性的更多代码,其中问题不存在.属性并不完全像一对方法.
Eri*_*ert 14
我猜测编译器首先尝试确定正在访问哪个属性,然后才检查它是get还是set.
你的猜测是正确的.
问题是 - 为什么?为什么不排除那些不提供吸气剂的候选人呢?为什么不解析实际的get/set方法并忽略它是属性的事实?
编译器可以解析所有候选属性,然后排除那些不提供get的属性 - 为什么不这样做呢? - sinelaw
因为语言设计师没有那样设计呢? - 罗伯特哈维
@sinelaw因为这不是C#语言的定义方式.它不是不能完成的,只是它没有完成. - user2864740
我敢肯定他们是这样设计的(他们不太可能忽视这种情况) - 但是这是什么原因? - sinelaw
@sinelaw大概是因为他们没有感觉到这样一个功能带来的好处会增加开发它的复杂性. - pswg
pswg在这里是正确的轨道,但我们可以更具体.
这里的基本设计原则是从内到外的分析,而不考虑"情境线索".当表达式的含义取决于它的直接上下文时,它对于读者来说既困惑又对编译器和IDE开发人员来说很难.我们想要做的是明确地计算每个表达式的含义,然后验证它在其上下文中是否有效.我们不想走另一条路,说"嗯,这个表达方式含糊不清,所以让我用上下文作为线索".
更具体而言:第一,编译器必须确定的含义a,然后a.Value,再确定分配是否合法.编译器并没有说"好吧,我无法弄清楚这两个属性中的哪一个a.Value意味着它是模棱两可的,但是我会假装我确实想到了这一点,然后回去修补我意识到我处于作业的价值方面,只有其中一个具有价值".编译器也没有说"当我在一个任务的左侧时,我将使用一个查找算法,而当我在右侧时,我将使用另一个查找算法".
(旁白:当然,我们在技术上讲不是在这里的赋值;我们在一个隐式类型本地的初始化器中,它不被归类为赋值运算符的用法.但它在逻辑上等同于这样,所以我们将让它通过而不做进一步的评论.)
这一一般规则有一些例外,针对特定的常见情况.编译器不知道例如,在形式的表达a.B()其B需要是可调用的东西; 成员查找算法会自动拒绝不可调用的成员而不会出错.Lambdas当然完全拒绝这个原则; lambda的含义完全取决于它的上下文.完成这项工作需要花费大量的工作 - 这是我对C#3的一个特性 - 我们进行了大量投资,以确保算法在常见场景中具有高性能.无论何时,当你从外到内,内到外的同时,你最终都处于潜在的指数状态,你必须进行所有可能的试验绑定,然后选择有效的独特试验.对于类型推断的lambdas这样的强大功能来说,这个成本是值得的.使其他形式的上下文敏感性工作,特别是对于您描述的模糊场景,并不是花费有限预算的好方法.
所以我的回答中的代码示例是有效的,因为它将两个属性定义"合并"到一个调用中(消除了编译器的歧义),同时为getter和setter实现了两个接口契约?罗伯特哈维
为了澄清,Robert删除的答案中的代码是:
public class GetSet : ISet, IGet
{
public string Value { get; set; }
}
...
getSet.Value = "This is a test";
Debug.Print(getSet.Value); //Prints "This is a test"
Run Code Online (Sandbox Code Playgroud)
罗伯特我不确定我理解你的问题.您的代码有效,因为首先,合同ISet和IGet履行完成.类GetSet具有每个所需的所有成员以及明确的映射.第二,因为您的呼叫站点根本不使用接口; 它只是直接调用类的成员.为什么它不起作用?
现在解决已删除答案中的一点:
只是拥有另一个继承原始的接口的接口不起作用,因为没有要绑定的支持字段.
不,这不是一个正确的分析.这与属性是否实际实现为编译器生成的字段无关.请记住,接口上的属性只是定义get_Value和set_Value方法的奇特方式.只要实现类中存在具有所需方法的属性,就满足接口要求.如何该属性实现高达类.
一个类属性满足两个不同合同的接口.
是! 那不是问题.只要可以明确地确定从接口成员到类/结构成员的映射,就可以了.例如:
interface IFoo
{
void M();
}
interface IBar
{
void M();
}
class C : IFoo, IBar
{
public void M() { }
}
Run Code Online (Sandbox Code Playgroud)
M可以做双重任务,因为IFoo.M和IBar.M.
遇到麻烦的地方是无法轻易确定哪种方法与界面匹配.有关详细信息,请参阅有关该主题的文章
http://blogs.msdn.com/b/ericlippert/archive/2006/04/05/odious-ambiguous-overloads-part-one.aspx
http://blogs.msdn.com/b/ericlippert/archive/2006/04/06/odious-ambiguous-overloads-part-two.aspx
对于一些有趣的相关恶作剧,看到这个问题和答案,卢西恩和我都解决了这个问题:
我同意你的分析.似乎编译器a.Value 在分析你如何使用它来将属性getter转换为调用之前需要解析符号get_Value.
值得注意的是,如果你这样做:
public void Bla(ISet a)
{
var x = a.Value;
}
Run Code Online (Sandbox Code Playgroud)
您没有收到" 不包含定义 "错误.你得到这个:
属性或索引器"ISet.Value"不能在此上下文中使用,因为它缺少get访问器
编译器找到符号,绑定它ISet.Value,然后才会抱怨它是如何被使用的(因为ISet它没有提供getter).
.NET的创建者已经决定,属性本身应该是一种特殊的实体,而不是仅仅允许对具有"属性"属性的适当命名方法进行替代形式的调用.这有时很烦人; 一个潜在有用的方面是它确保防止代码覆盖属性getter的可能性,而不会意识到属性也有一个setter,或者覆盖属性setter而没有意识到getter除了返回预期的后备字段之外还做了其他事情.
如果希望有一组协变的列表接口,则需要定义:
interface IReadableList<out T> {
T this[int index] { get; }
}
interface IWritableList<in T> {
T this[int index] { set; }
}
interface IMutableList<T>: IReadableList<T>, IWritableList<T> {
new T this[int index] { get; set; }
}
Run Code Online (Sandbox Code Playgroud)
反过来,代码必须定义所有三个属性的实现.C#中的隐式接口实现可以减轻必须定义三个属性的负担,因为定义的类T this[int index] {get {...}; set {...};}将使编译器使用指定的getter实现只读方法,使用指定setter的只写方法和读取-write方法同时使用两者,但从Framework的角度来看,实际上有三个独立的属性,而read/write属性的get/set独立于与read-only或write-相关的get/set属性.只有属性.
就我个人而言,我认为vb.net和C#都不愿意使用一个属性被读取的事实作为一个线索,即在重载解析中不应该考虑只写属性,这也很烦人.应该排除只读属性,但我没有设计这些语言,也没有设计框架.
| 归档时间: |
|
| 查看次数: |
1395 次 |
| 最近记录: |