C# 泛型集合中的多态类型参数

C# 泛型集合中的多态类型参数,c#,generics,C#,Generics,为什么C#编译器不允许在泛型集合(即列表[T])中使用多态类型(T)参数 以类“A”和“B”为例,其中“B”是“A”的子类 class A { } class B : A { } 并考虑一个函数,该函数使用类型“a”< /p> void f(List<A> aL) { } void f(List aL){} 使用类型为“B”的列表调用的 List<B> bL = new List<B>(); f(bL); List bL=新列表(); f(基本法);

为什么C#编译器不允许在泛型集合(即列表[T])中使用多态类型(T)参数

以类“A”和“B”为例,其中“B”是“A”的子类

class A { }
class B : A { }

并考虑一个函数,该函数使用类型“a”< /p>

void f(List<A> aL) { }
void f(List aL){}
使用类型为“B”的列表调用的

List<B> bL = new List<B>();

f(bL);
List bL=新列表();
f(基本法);
给出了以下错误

ERROR: cannot convert from List<B> to List<A>
错误:无法从列表转换为列表

违反了什么语义规则


除了循环和铸造每个元素(请给我一些糖)之外,还有什么“优雅”的意思吗?谢谢。

您的问题非常类似于: 答案是您不能执行该强制转换,因为这些类型是由模板类创建的不同类型,并且它们不继承。您可以做的是:

f(bL.Cast<A>());
f(bL.Cast());

就拿这个小例子来解释为什么这不起作用。假设我们有另一个
A
的子类型
C

class A {}
class B : A {}
class C : A {}

显然,我可以将
C
对象放入
列表中。

B
的集合传递给需要
a
集合的方法本身并没有什么问题。但是,根据您对该收藏的处理方式,可能会出现很多问题

考虑:

void f(List<A> aL)
{
    aL.(new A()); // oops! what happens here?
}

我认为您可能正在寻找“out”泛型修饰符,它允许两个泛型类型之间的协方差

该页面上发布的示例如下:

// Covariant delegate. 
public delegate R DCovariant<out R>();

// Methods that match the delegate signature. 
public static Control SampleControl()
{ return new Control(); }

public static Button SampleButton()
{ return new Button(); }

public void Test()
{            
    // Instantiate the delegates with the methods.
    DCovariant<Control> dControl = SampleControl;
    DCovariant<Button> dButton = SampleButton;

    // You can assign dButton to dControl 
    // because the DCovariant delegate is covariant.
    dControl = dButton;

    // Invoke the delegate.
    dControl(); 
}
//协变委托。
公共委托DCOR VARIANT();
//与委托签名匹配的方法。
公共静态控制SampleControl()
{返回新控件();}
公共静态按钮SampleButton()
{返回新按钮();}
公开无效测试()
{            
//用方法实例化委托。
DCOVERANT dControl=采样控制;
DCOARIANT dButton=采样按钮;
//您可以将dButton指定给dControl
//因为DCOVERANT委托是协变的。
dControl=dButton;
//调用委托。
dControl();
}
我不确定C目前是否支持其当前集合的协方差。

List
根本不是
List
的子类型。(我不确定什么是“协变”,什么是“逆变”,所以我会坚持“子类型”。
void Fun(List<A> aa) {
    aa(new A());
}

var bb = new List<B>();
Fun(bb); // whoopsie
并使用
可枚举
方法-如果基础类型是
列表
,则应优化大多数方法


不幸的是,由于C#泛型的工作原理,类根本不能是变量,只能是接口。据我所知,所有比IEnumerable更“丰富”的集合接口都是“读写”的。从技术上讲,您可以创建自己的协变包装器接口,该接口只公开所需的读取操作。

您的错误是B继承了A;但是
List
不继承自
List
<代码>列表!=A

您可以这样做:

List<A> aL = new List<A>();
aL.Add(new B());

f (aL)

“违反了什么语义规则?”-利斯科夫替换原则。你会发现许多关于协方差的好答案。在您的例子中,如果您将f更改为
void f(IEnumerable aL){}
,它将起作用,因为IEnumerable与A是协变的。如果它必须是一个列表,您可以使用扩展方法来避免显式地编写循环:
bL.Cast().ToList()
@Rake36我知道有区别,但在这种情况下,唯一的内置解决方案是“使用
IEnumerable
”,我相信在
in
out
修饰符出现在语言中之前,它一直是协变的,因此我的“应该不重要”“-在OP的用例中,这可能是可能的,但不太可能,因为他们没有对代表做任何事情。这个问题一天要问好几次。啊,我怀疑这一点,但不喜欢把我的问题和我自己假设的答案混在一起,所以没有提及。谢谢你,先生,谢谢你对这件事的深刻见解。这是一个很好的例子,当我有时间来探讨这个“安宁”的概念时,这应该会有很大帮助。谢谢Kenogu。+1你的“不同类型”解释给出了这不起作用的确切原因,所以谢谢你,还有很好的语法,我将尝试一下。@samusArin哇,我从未见过有人花这么多精力评论每个answe!就为了这个,我会搜索你给出的每一个好答案并+1。继续努力谢谢,但你真的不必。。。我这样做b/c我感谢所有有用的答案,即使我只能接受一个。我认为忽视帮助你的人是很不礼貌的,更不用说至少承认他们的帮助和努力了!!
void Fun(List<A> aa) {
    aa(new A());
}

var bb = new List<B>();
Fun(bb); // whoopsie
void Fun(IEnumerable<A> aa) { ... }
List<A> aL = new List<A>();
aL.Add(new B());

f (aL)
foreach(A a in list)
{
  if (a is B)
    //Do B stuff
  else
    //Do A stuff
}