null抽象基类/派生类的合并问题

时间:2013-11-14 10:02:05

标签: c# null coalescing

为什么C#null合并运算符无法解决这个问题?

  Cat c = new Cat();
  Dog d = null;

  Animal a = d ?? c;

这将给出错误

操作员?不能应用于Dog and Cat类型的操作数

鉴于以下编译,这似乎很奇怪。

Animal a = d;
a = c;

下面的上下文代码:

public abstract class Animal
{
  public virtual void MakeNoise()
  {        
    Console.WriteLine("noise");
  }    
}

public class Dog : Animal
{
  public override void MakeNoise()
  {
     Console.WriteLine("wuff");
  }
}

public class Cat : Animal
{
  public override void MakeNoise()
  {
    Console.WriteLine("miaow");
  }
}

4 个答案:

答案 0 :(得分:19)

C#的一个微妙的设计规则是C#永远不会推断出一个不在表达式中的类型。由于Animal不在表达式d ?? c中,因此类型Animal不是选择。

这个原则适用于C#推断类型的所有地方。例如:

var x = new[] { dog1, dog2, dog3, dog4, cat }; // Error

编译器没有说“这必须是一群动物”,它说“我认为你犯了一个错误”。

这是更一般的设计规则的特定版本,“当程序看起来不明确时给出错误而不是猜测可能是错误的”。

这里发挥作用的另一个设计规则是:从内到外的类型的原因,而不是从外到内的类型。也就是说,您应该能够通过查看部分来计算表达式中所有内容的类型,而无需查看其上下文。在您的示例中,Animal来自??表达式之外;我们应该能够弄清楚??表达式的类型是什么,然后问一个问题“这个类型是否与上下文兼容?”而不是走另一条路说“这里是上下文 - 现在计算出??表达式的类型。”

这条规则是合理的,因为上下文不清楚。在你的情况下,背景非常清楚;该东西被分配给Animal。但是怎么样:

var x = a ?? b;

现在推断出x的类型。我们不知道上下文的类型,因为这就是我们正在研究的内容。或

M(a ?? b)

M可能有二十多个重载,我们需要根据参数的类型知道要选择哪一个。很难用另一种方式来推理并说“上下文可能是这些内容之一;在每个上下文中评估a??b并计算出它的类型”。

lambdas违反了该规则, 根据其上下文进行分析。使代码正确和有效是非常困难的;它花了我一年的工作中最好的一部分。编译器团队可以更快,更好地完成更多功能,而不需要在不需要的情况下承担这些费用。

答案 1 :(得分:9)

分配给Animal a之前,c和d仍分别为CatDog。以下方法按照您的预期方式工作:

Animal a = (Animal)c ?? (Animal)d;

答案 2 :(得分:5)

出于同样的原因Animal a = (true)? d : c;不起作用(使用三元运算符)。

根据C#规范,表达式的类型推断如下(引用Eric Lippert):

  

?:运算符的第二个和第三个操作数控制的类型   条件表达式。设X和Y为第二个和第二个的类型   第三个操作数。然后,

     
      
  • 如果X和Y是相同的类型,则这是条件表达式的类型。
  •   
  • 否则,如果从X到Y存在隐式转换,但不存在从Y到X的隐式转换,则Y是条件表达式的类型。
  •   
  • 否则,如果从Y到X存在隐式转换,但不存在从X到Y的隐式转换,则X是条件表达式的类型。
  •   
  • 否则,无法确定表达式类型,并发生编译时错误。
  •   

由于从狗到猫,从猫到狗都没有隐式转换,因此无法推断出类型。 同样的原则适用于空合并运算符。

修改

  

为什么null合并关心猫与狗之间的关系,而不仅仅关注猫与动物,狗与动物之间的关系?

至于为什么编译器不只是意识到两个运算符都是Animal s:

  

它只是一大堆蠕虫。我们喜欢这种类型的原则   表达式必须是表达式中某事物的类型。

答案 3 :(得分:2)

失败,因为c无法隐式转换为d,反之亦然。 Obvoiously Cat不是DogDog也不是Cat

试试这个

Animal a = (Animal)d ?? c;

现在我们说??左侧操作数的编译器是Animal,是的,它可以将“dog to animal”转换为??的右侧和右侧操作数Cat 1}}也可以转换为“动物”。编译器现在很高兴:)