具有继承性的匿名类型列表

A list of anonymous types with inheritance

假设我有以下 classes

public class Dog{}
public class Bulldog : Dog{}
public class Pitbull : Dog{}

我正在尝试创建这样的列表

var dogs2 = new[]
{
   new { X = new Bulldog(), Y = 10},
   new { X = new Pitbull(), Y = 20}
}.ToList();

但是,我收到以下错误 No best type found for implicitly - typed array
好的,我想这是有道理的。系统无法识别 X is a type ofDog`

所以,我尝试这样做

var dogs1 = Enumerable.Empty<object>()
                      .Select(x => new { X = new Dog (), Y = 0 })
                      .ToList();
dogs1.Add(new { X = new Pitbull(), Y = 10 });
dogs1.Add(new { X = new Bulldog(), Y = 10 });

现在,我收到此错误 cannot convert from '<anonymous type: PassingEvents.Bulldog X, int Y>' to '<anonymous type: PassingEvents.Dog X, int>'。为什么不能转换? Bulldog 不是可以转换为 Dog 吗?

有没有办法解决这个问题,而不必创建一个新的 class?下面的代码工作得很好

public class DogCombo
{
    public Dog X;
    public int Y;
}

var dogs3 = new[]
{
    new DogCombo{ X = new Bulldog(), Y = 10},
    new DogCombo{ X = new Pitbull(), Y = 20}
 }.ToList();

您可以明确地将 X 转换为 Dog 的类型:

var dogs2 = new[]
{
   new { X = (Dog)new Bulldog(), Y = 10},
   new { X = (Dog)new Pitbull(), Y = 20}
}.ToList();

Why can't it convert? Isn't Bulldog castable to Dog?

Bulldog 可以转换为 Dog,是的。

你的推理如下:

  • Bulldog 可以转换为 Dog
  • 因此 anonymous-type-with-Bulldog 应该可以转换为 anonymous-type-with-Dog。

这种推理——基础类型的属性应该暗示类型在转换下的属性——被称为协方差

C# 在某些受限情况下支持协变。他们是:

  • Bulldog 数组可以用作 Dog 数组。请注意,这是 不安全协方差 ,因为您可以将 Poodle 放入 Dog 数组中,但不能放入 Bulldog 数组中。只有当你从不写入数组时,数组协方差才是安全的。

  • 一个IEnumerable<Bulldog>可以转换为IEnumerable<Dog>。这是安全的。斗牛犬的序列是狗的序列。其他一些泛型类型也是如此,例如 IEnumerator<T>.

  • 一种方法,可以将 returns Bulldog 转换为 returns Dog 的委托类型的委托。

C# 在某些情况下也支持 逆变。例如,IComparable<Dog> 可能会转换为 IComparable<Bulldog>。可以与狗相提并论的事物也可以与斗牛犬相提并论。请注意,这种可兑换性是朝着相反的方向发展的,因此 contra 变体。

C# 支持 classesstructs[ 上的任何协变(或逆变) =61=] 或 匿名类型

理论上,C# 可以根据需要支持匿名类型的逆变;这样做是安全的。语言和运行时团队之前已经介绍过此功能。这只是语言和运行时团队没有足够高地优先考虑功能以实际实现它的情况。

如果您认为在匿名类型等结构类型上实现协变转换确实有充分的理由,您可以在 Roslyn github 论坛上就该主题展开讨论,也许它会被实现总有一天。

Is there a way to fix this, without having to create a new class?

我建议您创建一个新的 class。但是,如果您想要一种便宜且简单的解决方法:只需将 显式 转换为 Dog.