我有一个继承自 IList 的接口:
public interface IBase {}
public class Derived : IBase {}
public interface IMyList : IList<IBase> {}
我想将一个类型的变量IMyList
转换为 type IList<Derived>
or List<Derived>
,以更容易或最有意义的为准。最好的方法是什么?
请注意,我使用的是 .NET 3.5
我有一个继承自 IList 的接口:
public interface IBase {}
public class Derived : IBase {}
public interface IMyList : IList<IBase> {}
我想将一个类型的变量IMyList
转换为 type IList<Derived>
or List<Derived>
,以更容易或最有意义的为准。最好的方法是什么?
请注意,我使用的是 .NET 3.5
直接强制转换不适合,因为可能会出现许多问题(IMyList
可能包含除 等之外Derived
的类型)
只要您不修改列表(在这种情况下IEnumerable<Derived>
可行),您可以简单地:
var validItems = myList.Cast<Derived>();
然后迭代结果。
编辑
根据以下OP的评论,还有两件事需要提及:
1)如果你需要一个IList<Derived>
,你可以简单地添加到上面并调用myList.Cast<Derived>().ToList();
2)如果这些确实是要求,那么您的代码没有任何意义。如果IMyList
应该只包含Derived
对象,那么IMyList
应该派生自IList<Derived>
. 请记住,虽然您知道只有一种类型实现了IBase
接口,但编译器并没有那么具体。
只是为了使用接口而到处使用接口对任何人都没有帮助!
我将转换后的结果传递给一个接受
IList<Derived>
.
然后你就进入了一个你自己设计的痛苦世界。我的建议是首先找到解决循环依赖问题的其他方法。正如您所发现的,将所有内容都变成只有一个可能实现的接口是解决该问题的一种痛苦方法。我不推荐它。
如果你不能这样做,那么我会尝试修复有问题的函数,以便它接受一个IList<IBase>
,一个IEnumerable<IBase>
或一个IEnumerable<Derived>
。最好是 IEnumerable 解决方案之一;大多数采用列表的方法实际上只需要序列。如果您可以在此处解释为什么需要列表,这将有助于尝试找到解决方法。
如果你能做一个IList<IBase>
或,IEnumerable<IBase>
那么你就完成了;您已经有了可以隐式转换为所需类型的东西。
如果你可以让它采取,IEnumerable<Derived>
那么你可以说myListOfIBase.Cast<Derived>()
(如果你真的知道它们都是派生的)或myListOfIBase.OfType<Derived>()
(如果你怀疑其中一些可能不是派生类型并想跳过它们)并得到一个IEnumerable<Derived>
有效使用的基础列表。
如果您无法更改有问题的功能,那么您可以创建自己的类来有效地使用基础列表:
sealed class MyProxyList : IList<Derived>
{
IList<IBase> underlyingList;
public MyProxyList(IList<IBase> underlyingList)
{
this.underlyingList = underlyingList;
}
... now implement every member of IList<Derived> as
... a call to underlyingList with a cast where necessary
}
然后将一个新的传递MyProxyList
给该方法。
.Net 3.5 没有通用协方差。展示:
[TestFixture]
class Class1
{
[Test]
public void test()
{
var list = new List<SuperClass>();
list.Add(new SuperClass());
var castedList = ((List<BaseClass>)list);
}
}
public class BaseClass
{
public string a { get; set; }
}
public class SuperClass : BaseClass
{
public string b { get; set; }
}
不会编译成功。
在贾斯汀·尼斯纳(Justin Niessner)的回应中,围绕
var validItems = myList.Cast<Derived>();
被张贴。这将起作用,但会导致整个列表的枚举(尽管此枚举被延迟)并且还返回一个可枚举的。要转换列表并以 IList 结尾,您可以使用以下命令
[Test]
public void CanConvertListToBaseClass()
{
var list = new List<SuperClass>();
list.Add(new SuperClass());
var castedList = list.Cast<BaseClass>().ToList();
Assert.That(castedList, Is.InstanceOf<IList<BaseClass>>());
}
然而,这是一种相当蛮力的方法。这将产生一个新的单独的 IList,并将强制枚举整个列表。
由于IList<T>
允许类型的读写操作T
,接口既不是协变的也不是逆变的。因此,您想要的东西无法完成。想象一下下面的代码:
var myList = new MyListImlementation();
myList.Add(new BaseImplementation());
var castList = (IList<Derived>)myList; // this is what you want
// this would break, because myList contains elements of type BaseImplementation.
Derived d = castList[0];
如果您可以绝对确定所有实例都将是 type Derived
,那么您可以使用Cast<>()
,并解决前面提到的性能问题。如果有可能其他东西Derived
会出现在列表中,那么你会想要使用它OfType<Derived>()
。非Derived
物品将被排除而不是扔掉。
您不能在 .Net 3.5 中执行此操作,但可以在 .Net 4.0 中执行此操作。