虽然可以从基类/接口继承,但为什么不能声明List<>呢
使用相同的类/接口?
interface A
{ }
class B : A
{ }
class C : B
{ }
class Test
{
static void Main(string[] args)
{
A a = new C(); // OK
List<A> listOfA = new List<C>(); // compiler Error
}
}
有别的办法吗?
首先,停止使用难以理解的类名,如A, B, c,使用动物,哺乳动物,长颈鹿,或食物,水果,橙子或其他关系清楚的东西。
你的问题是"既然我可以把长颈鹿赋值给动物类型的变量,为什么我不能把长颈鹿的列表赋值给动物类型的变量?"
答案是:假设你可以。那么会出什么问题呢?
嗯,你可以在动物列表中添加一只老虎。假设我们允许您将一个长颈鹿列表放入一个包含动物列表的变量中。然后你试着在名单上加上一只老虎。会发生什么呢?你想在长颈鹿的名单中包含一只老虎吗?你想撞车吗?或者你希望编译器通过在第一时间使赋值非法来保护你不崩溃?
我们选择后者。
这种转换称为“协变”转换。在c# 4中,我们将允许您在接口和委托上进行协变转换,前提是这种转换总是安全的。详见我关于协方差和逆变的博客文章。(这周的周一和周四会有一个新的话题。)
首先,停止使用难以理解的类名,如A, B, c,使用动物,哺乳动物,长颈鹿,或食物,水果,橙子或其他关系清楚的东西。
你的问题是"既然我可以把长颈鹿赋值给动物类型的变量,为什么我不能把长颈鹿的列表赋值给动物类型的变量?"
答案是:假设你可以。那么会出什么问题呢?
嗯,你可以在动物列表中添加一只老虎。假设我们允许您将一个长颈鹿列表放入一个包含动物列表的变量中。然后你试着在名单上加上一只老虎。会发生什么呢?你想在长颈鹿的名单中包含一只老虎吗?你想撞车吗?或者你希望编译器通过在第一时间使赋值非法来保护你不崩溃?
我们选择后者。
这种转换称为“协变”转换。在c# 4中,我们将允许您在接口和委托上进行协变转换,前提是这种转换总是安全的。详见我关于协方差和逆变的博客文章。(这周的周一和周四会有一个新的话题。)
这是对BigJim的精彩回答的延伸。
在我的例子中,我有一个带有子字典的NodeBase类,我需要一种方法来从子字典中进行O(1)查询。我试图在Children的getter中返回一个私有字典字段,所以显然我想避免昂贵的复制/迭代。因此,我使用Bigjim的代码将Dictionary<whatever特定类型>转换为generic Dictionary<NodeBase>:
// Abstract parent class
public abstract class NodeBase
{
public abstract IDictionary<string, NodeBase> Children { get; }
...
}
// Implementing child class
public class RealNode : NodeBase
{
private Dictionary<string, RealNode> containedNodes;
public override IDictionary<string, NodeBase> Children
{
// Using a modification of Bigjim's code to cast the Dictionary:
return new IDictionary<string, NodeBase>().CastDictionary<string, RealNode, NodeBase>();
}
...
}
这很有效。然而,我最终遇到了不相关的限制,并最终在基类中创建了一个抽象的FindChild()方法来代替它进行查找。事实证明,这从一开始就消除了对强制转换字典的需求。(出于我的目的,我可以用一个简单的IEnumerable替换它。)
所以你可能会问的问题(特别是当性能问题禁止你使用.Cast<>或.ConvertAll<>)是:
“我真的需要强制转换整个集合吗?或者我可以使用抽象方法来保存执行任务所需的特殊知识,从而避免直接访问集合吗?”
有时候最简单的解决方案就是最好的。