C# - 有什么方法可以转换泛型集合吗?

标签 c# .net c#-4.0 generics casting

我一直忙于 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 add Tree<Banana> instances. It will than of course say that there is no implicit reference between Tree<Fruit> and Tree<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 更具体的类型,例如 TreeICollectionIEnumerable 。)

我会更进一步,将该逻辑封装在 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/

相关文章:

.net - 使用 AfterTargets 的 MSBuild 任务在转换后加密 web.config

c# - 对于任何值类型 T,如何确定对象的类型是否是 IEnumerable<T> 的子类?

c# - 字符串格式参数中的逗号

c# - 什么是对话上下文?

c# - 返回从存储过程中仅一个命令删除的行数

c# - AdjustThreadsInPool , 线程中止异常

c# - 如何查找System.Private.CoreLib.ni.dll中发生 'System.Runtime.InteropServices.SEHException'的原因?

c# - 错误: The cast to value type 'System.Int32' failed because the materialized value is null

c# - C# 中的动态和泛型

c#-4.0 - 如何在 .net 4.0 中设置 configSection