C# 当矛盾导致歧义时,没有警告或错误(或运行时故障)
首先,请记住.NETC# 当矛盾导致歧义时,没有警告或错误(或运行时故障),c#,.net,undefined-behavior,contravariance,ambiguity,C#,.net,Undefined Behavior,Contravariance,Ambiguity,首先,请记住.NET字符串既是可转换的也是可克隆的 现在,考虑下面的非常简单的代码: //contravariance "in" interface ICanEat<in T> where T : class { void Eat(T food); } class HungryWolf : ICanEat<ICloneable>, ICanEat<IConvertible> { public void Eat(IConvertible convert
字符串
既是可转换的
也是可克隆的
现在,考虑下面的非常简单的代码:
//contravariance "in"
interface ICanEat<in T> where T : class
{
void Eat(T food);
}
class HungryWolf : ICanEat<ICloneable>, ICanEat<IConvertible>
{
public void Eat(IConvertible convertibleFood)
{
Console.WriteLine("This wolf ate your CONVERTIBLE object!");
}
public void Eat(ICloneable cloneableFood)
{
Console.WriteLine("This wolf ate your CLONEABLE object!");
}
}
用以下方法进行测试:
static void Main()
{
var w2 = new Wolf2();
w2.Eat(new StackOverflowException()); // OK, one overload is more "specific" than the other
ICanEat<StackOverflowException> w2Soe = w2; // Contravariance
w2Soe.Eat(new StackOverflowException()); // Depends on interface order in Wolf2
}
static void Main()
{
var w2=新的Wolf2();
Eat(新的StackOverflowException());//好的,一个重载比另一个重载更“特定”
ICanEat w2Soe=w2;//相反
Eat(新的StackOverflowException());//取决于Wolf2中的接口顺序
}
仍然没有警告、错误或异常。仍然取决于类
声明中的接口列表顺序。但我认为更糟糕的原因是,这一次有人可能会认为重载解析总是选择SystemException
,因为它比Exception
更具体
悬赏开启前的状态:来自两个用户的三个答案
悬赏最后一天的情况:仍然没有收到新的答复。如果没有答案出现,我将不得不将奖金奖励给穆斯林Ben Dhaou。在这种情况下无法生成编译器错误,因为代码是正确的,并且对于所有不同时从两个内部类型继承的类型,它应该运行良好。如果同时从类和接口继承,则问题是相同的。(即在代码中使用base
object
class)
出于某种原因,VB.Net编译器在这种情况下会抛出类似于的警告
由于“接口ICustom(Of In T)”中的“In”和“Out”参数,接口“ICustom(Of Foo)”与另一个实现的接口“ICustom(Of Boo)”不明确
我同意C#编译器也应该发出类似的警告。看看这个。Lippert先生确认运行时将选择一个,并且应该避免这种编程。这里有一个尝试,为我刚刚想到的没有警告或错误的理由,我甚至没有喝酒 进一步抽象代码,目的是什么
ICanEat<string> beast = SomeFactory.CreateRavenousBeast();
beast.Eat("sheep");
i吃野兽=SomeFactory.CreateRavenousBeast();
野兽。吃(“羊”);
你在喂东西。野兽是如何把它吃掉的。如果给它喝汤,它可能会决定用勺子。它可能用刀叉来吃羊。它可能会用牙齿把它撕成碎片。但关键是作为这个类的调用者,我们不在乎它吃什么,我们只想让它吃东西
最终是由野兽决定如何吃它得到的东西。如果野兽决定它可以吃掉一个iclonable
和一个IConvertible
,那么它就要决定如何处理这两种情况。为什么猎场看守人要关心动物如何进食
如果它确实导致编译时错误,那么实现一个新接口就是一个破坏性的更改。例如,如果您将HungryWolf
作为icanat
发货,然后在几个月后添加icanat
,则会破坏它与实现两个接口的类型一起使用的每个位置
这是双向的。如果泛型参数只实现了
ICloneable
,那么它可以很好地与wolf一起工作。测试通过了,把它运过来,非常感谢你,沃尔夫先生。明年,您将为您的类型添加IConvertible
支持,然后砰的一声!错误真的没有那么大帮助。另一种解释是:没有歧义,因此没有编译器警告
请容忍我
ICanEat<string> wolf = new HungryWolf();
wolf.Eat("sheep");
饥饿狼是不明确的,不是我吃的。在预期编译器错误时,您要求编译器在调用Eat
时查看变量的值,并确定它是否不明确,这不一定是它可以推断的。考虑一个类<代码> UnambiguousCow <代码>,它只实现<代码> ICANATE/<代码> .<
我吃野兽;
如果(某些不可预测的)
beast=新饥饿狼();
其他的
beast=new UnambiguousCow();
野兽。吃(“晚餐”);
您希望在何处以及如何引发编译器错误?我相信编译器在VB.NET中通过警告做得更好,但我仍然认为这还不够。不幸的是,“正确的事情”可能需要禁止某些可能有用的东西(使用两个协变或逆变泛型类型参数实现相同的接口),或者为语言引入一些新的东西 目前,除了HungryWolf类之外,编译器现在无法分配错误。在这一点上,一个类声称知道如何做一些可能模棱两可的事情。它在陈述 我知道如何吃
ICloneable
,或者以某种方式实现或继承它的任何东西
而且,我还知道如何以某种方式吃IConvertible
,或者任何实现或继承它的东西
然而,如果它的盘子上有既可以IClonable
又可以IConvertible
的东西,它从不说明应该做什么。如果给编译器一个HungryWolf
的实例,这不会给编译器带来任何麻烦,因为它可以肯定地说“嘿,我不知道在这里做什么!”。但是当它被赋予icanat
实例时,它会给编译器带来痛苦编译器不知道变量中对象的实际类型是什么,只是它确实实现了ICANAT
不幸的是,当HungryWolf存储在该变量中时,它模糊地实现了完全相同的接口两次。因此,我们当然不能在尝试调用icanat.Eat(string)
时抛出错误,因为该方法存在,并且对于可以放入icanat中的许多其他对象都完全有效<
ICanEat<string> beast = SomeFactory.CreateRavenousBeast();
beast.Eat("sheep");
ICanEat<string> wolf = new HungryWolf();
wolf.Eat("sheep");
HungryWolf wolf = new HungryWolf();
wolf.Eat("sheep");
ICanEat<string> beast;
if (somethingUnpredictable)
beast = new HungryWolf();
else
beast = new UnambiguousCow();
beast.Eat("dinner");
class HungryWolf:
ICanEat<ICloneable>,
ICanEat<IConvertible>,
ICanEat<TGenerated_ICloneable_IConvertible>
where TGenerated_ICloneable_IConvertible: IConvertible, ICloneable {
// implementation
}
.class auto ansi nested private beforefieldinit HungryWolf
extends
[mscorlib]System.Object
implements
class NamespaceOfApp.Program/ICanEat`1<class [mscorlib]System.ICloneable>,
class NamespaceOfApp.Program/ICanEat`1<class [mscorlib]System.IConvertible>,
class NamespaceOfApp.Program/ICanEat`1<class ([mscorlib]System.IConvertible, [mscorlib]System.ICloneable>)!TGenerated_ICloneable_IConvertible>
interface ICanReturn<out T> where T: class {
}
class ReturnStringsOrCharArrays:
ICanReturn<string>,
ICanReturn<char[]>,
ICanReturn<TGenerated_String_ArrayOfChar>
where TGenerated_String_ArrayOfChar: object|ICloneable|IEnumerable<char> {
}