- c - 在位数组中找到第一个零
- linux - Unix 显示有关匹配两种模式之一的文件的信息
- 正则表达式替换多个文件
- linux - 隐藏来自 xtrace 的命令
我一直忙于 C# 4.0 泛型,现在我基本上想做这样的事情:
public abstract class GenericTree<T> : Tree
where T : Fruit
{
public Tree(IFruitCollection<T> fruits)
: base(fruits) { }
}
基类 Tree 类如下所示:
public abstract class Tree
{
private IFruitCollection<Fruit> fruits;
public IFruitCollection<Fruit> GetFruits
{
get { return fruits; }
}
public Tree(IFruitCollection<Fruit> fruits)
{
this.fruits = fruits;
}
}
这是我的第一个问题。 GenericTree 的构造函数无法将泛型集合转换为水果集合。我还有一个 GenericTree 的实现:
public class AppleTree : GenericTree<Apple>
{
public AppleTree()
: base(new FruitCollection<Apple>) { }
}
这是我的第二个问题。当我使用 myAppleTree.GetFruits.Add(...) 将水果添加到 AppleTree 的实例时,我不仅限于苹果。我可以添加各种水果。我不想要这个。
我试图通过将其添加到 GenericTree 来解决该问题:
new public IFruitCollection<T> GetFruits
{
get { return base.GetFruits as IFruitCollection<T>; }
}
但这也是不可能的。它总是以某种方式返回 null。当我的第一个问题得到解决时,这可能会得到解决。
IFruitCollection 界面如下所示:
public interface IFruitCollection<T> : ICollection<T>
where T : Fruit { ... }
而 FruitCollection 类是 Collection 类的简单实现。哦,当然,Apple 类扩展了 Fruit 类。
解决方案是让 IFruitCollection 接口(interface)同时兼容协变和逆变。但是我该如何实现呢? “in”或“out”参数关键字是不可能的,因为 ICollection 接口(interface)不允许。
非常感谢您的帮助!
最佳答案
回应您的评论:
At some point, I need a list of Trees. And a list of
Tree<Fruit>
doesn't do it, because doesn't allow me to addTree<Banana>
instances. It will than of course say that there is no implicit reference betweenTree<Fruit>
andTree<Banana>
.
基本问题是您想要一个集合(树的列表)(间接地)包含不同类型的相似对象。为了从 Tree
中消除该集合的歧义。 (这也是 Fruit
的集合),我们称它为 Orchard
.
Orchard -(contains)-> Tree -(contains)-> Fruit
如果你制作一个非泛型 Tree
, Orchard
的元素可能都是那种类型。但是正如您所注意到的,这意味着您最终会遇到树类型不安全的问题,并且您可以将香蕉放在苹果树中。您必须通过 Tree
中的运行时类型检查来解决该问题实现。
或者,您可以制作一个通用的 Tree<T> where T : Fruit
类,因此对于 Fruit
的子类具有类型安全性包含在树中。这意味着 Orchard
将包含不同类型的对象,同样需要进行运行时类型检查。
(您可以通过为每种类型声明一个单独的访问器来创建具有静态类型安全的 Orchard:
class Tree { }
class Tree<T> : Tree { }
class Trees : IEnumerable<Tree>
{
Tree<Banana> _bananaTree;
Tree<Apple> _appleTree;
//...etc.
Tree<Banana> GetBananaTree() { return _bananaTree; }
Tree<Apple> GetBananaTree() { return _appleTree; }
//...etc.
public IEnumerator<Tree> GetEnumerator()
{
yield return _bananaTree;
yield return _appleTree;
//...etc.
}
}
但这可能不是你想要的,所以你需要在某处进行转换。)
我假设您宁愿在 Orchard
中进行类型转换比Tree
,在这种情况下,我会建议这样的基本方法:
IDictionary<Type, object> orchard = new Dictionary<Type, object>();
//to retrieve the tree
Tree<Banana> bananaTree = (Tree<Banana>)orchard[typeof(Banana)];
(当然,您可以使用比 object
更具体的类型,例如 Tree
或 ICollection
或 IEnumerable
。)
我会更进一步,将该逻辑封装在 Orchard
中类,它可以通过在访问器中执行转换来提供更简洁的语法:
var bananaTree = orchard.GetTree<Banana>();
哪里:
public class Orchard
{
private IDictionary<Type, object> _trees;
//...
public Tree<T> GetTree<T>()
{
return (Tree<T>)_trees[typeof(T)];
}
}
最终,这是一个很好的例子,说明为什么接口(interface)中的协变和逆变类型参数必须分别限制在输出和输入位置。对于既用作输入又用作输出的类型,您最终需要进行运行时类型检查。
关于C# - 有什么方法可以转换泛型集合吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9115593/
我是一名优秀的程序员,十分优秀!