我在尝试将AddRange(IEnumerable)添加到List时遇到了麻烦。可能是一个经典问题,但我还没有真正得到它。
我知道期望List参数的方法不满足于List,因为他们可能会尝试向List添加Base,这显然是不可能的。
但是,如果我得到正确的,因为IEnumerables本身不能改变,它应该在这种情况下工作。
我想到的代码看起来像这样:
class Foo
{
}
class Bar : Foo
{
}
class FooCol
{
private List<Foo> m_Foos = new List<Foo> ();
public void AddRange1(IEnumerable<Foo> foos)
{
m_Foos.AddRange (foos); // does work
}
public void AddRange2<T>(IEnumerable<T> foos) where T : Foo
{
m_Foos.AddRange (foos); // does not work
}
}
class Program
{
static void Main(string[] args)
{
FooCol fooCol = new FooCol ();
List<Foo> foos = new List<Foo> ();
List<Bar> bars = new List<Bar> ();
fooCol.AddRange1 (foos); // does work
fooCol.AddRange1 (bars); // does not work
fooCol.AddRange2 (foos); // does work
fooCol.AddRange2 (bars); // does work
}
}
我试图在AddRange2方法中向编译器传递一个提示,但这只是问题所在。
我的思维方式有缺陷吗?这是语言的限制还是设计?
IIRC,Java 1.5中添加了对此类操作的支持,所以也许它将来会在某个时候被添加到C#中......?答案 0 :(得分:18)
这是协方差,将在C#4.0 / .NET 4.0中修复。目前,通用选项是最佳答案(适用于IEnumerable<T>
- not IList<T>
etc)。
但是在通用方法中,你必须考虑T
。您还可以使用Cast<T>
或OfType<T>
与LINQ实现类似的目标。
答案 1 :(得分:2)
在C#3.0中,您可以使用“Cast”扩展方法。如果您导入System.Linq然后使用此代码:
public void AddRange2<T>(IEnumerable<T> foos) where T : Foo
{
m_Foos.AddRange (foos.Cast<Foo>());
}
然后它应该适合你。
答案 2 :(得分:0)
扩展方法有解决方法:
public static IEnumerable<TBase> ToBaseEnumerable<TBase, TDerived>( this IEnumerable<TDerived> items ) where TDerived : TBase {
foreach( var item in items ) {
yield return item;
}
}
...
IEnumerable<Employee> employees = GetEmployees(); //Emplyoee derives from Person
DoSomethingWithPersons( employees.ToBaseEnumerable<Person, Employee>() );
但是“&lt; Person,Employee&gt;”有点尴尬:/。
答案 3 :(得分:0)
当然,强制转换解决方案可能会生成类强制转换异常。 发布可枚举扩展工作的人说这很尴尬。 我想出了一个只有一半尴尬的解决方案,不知道我是否会使用它:
public static class UpTo<T>
{
public static IEnumerable<T> From<F>(IEnumerable<F> source) where F:T
{
// this cast is guaranteed to work
return source.Select(f => (T) f);
}
}
用法:
IEnumerable哺乳动物= UpTo&lt; Mammal&gt;。(kennel.Dogs)