Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/.net/25.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/unix/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 当矛盾导致歧义时,没有警告或错误(或运行时故障)_C#_.net_Undefined Behavior_Contravariance_Ambiguity - Fatal编程技术网

C# 当矛盾导致歧义时,没有警告或错误(或运行时故障)

C# 当矛盾导致歧义时,没有警告或错误(或运行时故障),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

首先,请记住.NET
字符串
既是
可转换的
也是
可克隆的

现在,考虑下面的非常简单的代码:

//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> {
}