Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/.net/22.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C#-有没有办法铸造一个通用集合?_C#_.net_C# 4.0_Generics_Casting - Fatal编程技术网

C#-有没有办法铸造一个通用集合?

C#-有没有办法铸造一个通用集合?,c#,.net,c#-4.0,generics,casting,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) { } } public abstract class Tree { private IFruitCollection<Fruit>

我一直忙于C#4.0泛型,现在我基本上想做这样的事情:

public abstract class GenericTree<T> : Tree
    where T : Fruit
{
    public Tree(IFruitCollection<T> fruits)
        : base(fruits) { }
}
public abstract class Tree
{
    private IFruitCollection<Fruit> fruits;

    public IFruitCollection<Fruit> GetFruits
    {
        get { return fruits; }
    }

    public Tree(IFruitCollection<Fruit> fruits)
    {
        this.fruits = fruits;
    }
}
public interface IFruitCollection<T> : ICollection<T>
    where T : Fruit { ... }
公共抽象类GenericTree:树
T:水果
{
公共树木(IFruitCollection果实)
:基(果){}
}
基本树类如下所示:

public abstract class GenericTree<T> : Tree
    where T : Fruit
{
    public Tree(IFruitCollection<T> fruits)
        : base(fruits) { }
}
public abstract class Tree
{
    private IFruitCollection<Fruit> fruits;

    public IFruitCollection<Fruit> GetFruits
    {
        get { return fruits; }
    }

    public Tree(IFruitCollection<Fruit> fruits)
    {
        this.fruits = fruits;
    }
}
public interface IFruitCollection<T> : ICollection<T>
    where T : Fruit { ... }
公共抽象类树
{
私家采果;
公共IFruitCollection GetFruits
{
获取{返回水果;}
}
公共树木(IFruitCollection果实)
{
这个。水果=水果;
}
}
这是我的第一个问题。GenericTree的构造函数无法将泛型集合强制转换为水果集合。我还实现了GenericTree:

public class AppleTree : GenericTree<Apple>
{
    public AppleTree()
        : base(new FruitCollection<Apple>) { }
}
new public IFruitCollection<T> GetFruits
{
    get { return base.GetFruits as IFruitCollection<T>; }
}
公共类AppleTree:GenericTree
{
公共AppleTree()
:base(新集合){}
}
这是我的第二个问题。当我使用myAppleTree.GetFruits.add(…)将水果添加到AppleTree实例时,我不仅限于苹果。我可以添加各种水果。我不要这个

我试图通过在GenericTree中添加以下内容来解决该问题:

public class AppleTree : GenericTree<Apple>
{
    public AppleTree()
        : base(new FruitCollection<Apple>) { }
}
new public IFruitCollection<T> GetFruits
{
    get { return base.GetFruits as IFruitCollection<T>; }
}
新公共IFruitCollection GetFruits
{
获取{return base.GetFruits as IFruitCollection;}
}
但这也不可能。它总是以某种方式返回null。当我的第一个问题得到解决时,这个问题有可能得到解决

IFruitCollection接口如下所示:

public abstract class GenericTree<T> : Tree
    where T : Fruit
{
    public Tree(IFruitCollection<T> fruits)
        : base(fruits) { }
}
public abstract class Tree
{
    private IFruitCollection<Fruit> fruits;

    public IFruitCollection<Fruit> GetFruits
    {
        get { return fruits; }
    }

    public Tree(IFruitCollection<Fruit> fruits)
    {
        this.fruits = fruits;
    }
}
public interface IFruitCollection<T> : ICollection<T>
    where T : Fruit { ... }
公共接口IFruitCollection:icCollection
其中T:水果{…}
而水果收集类是收集类的一个简单实现。 当然,苹果类扩展了水果类

解决方案是使IFruitCollection接口同时兼容协方差和反方差。但我如何做到这一点?“in”或“out”参数关键字是不可能的,因为ICollection接口不允许这样做


非常感谢你的帮助

回应您的评论:


在某个时候,我需要一个树的列表。而
树的列表
不起作用,因为不允许我添加
实例。当然,它会说
Tree
Tree
之间没有隐含的引用

基本问题是,您希望有一个集合(树列表),它(间接地)包含不同类型的类似对象。为了从
(也是
水果
的一个集合)中消除该集合的歧义,我们将其称为
果园

果园-(包含)->树-(包含)->水果

如果创建非泛型的
,则
Orchard
的元素都可能属于该类型。但正如你所注意到的,这意味着你最终会遇到这样一个问题:树不是类型安全的,你可以把香蕉放在苹果树上。您必须通过
实现中的运行时类型检查来解决该问题

或者,您可以创建一个通用的
树,其中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.
    }
}
(当然,您可以使用比
对象
更具体的类型,如
,或
ICollection
,或
IEnumerable

我将更进一步,将该逻辑封装在
Orchard
类中,通过在访问器中执行强制转换,该类可以提供不太详细的语法:

var bananaTree = orchard.GetTree<Banana>();
var bananaTree=orchard.GetTree();
其中:

public class Orchard
{
    private IDictionary<Type, object> _trees;

    //...

    public Tree<T> GetTree<T>()
    {
        return (Tree<T>)_trees[typeof(T)];
    }
}
公共类乌节
{
私人索引树;
//...
公共树GetTree()
{
返回(树)_树[类型(T)];
}
}

最后,这是一个很好的例子,说明了为什么接口中的协变和逆变类型参数必须分别限制在输出和输入位置。对于同时用作输入和输出的类型,您最终需要进行运行时类型检查。

我想我找到了解决方案,当时我正在尝试phoog的解决方案。谢谢,phoog!

起初我没有给GenericTree他自己的水果集合。Mosty是因为基类经常循环他自己的集合,例如更新水果。这导致水果不更新,因为它们被添加到GenericTree的集合,而不是基集合

但最终我意识到,铸造这些收藏品也永远不会奏效

现在我已经创建了另一个FruitCollection类,它可以自动向基础FruitCollection添加和删除组件,反之亦然。这个解决方案对我来说非常有用

public FruitCollection<T, U> : FruitCollection<T>
    where T : U
    where U : Fruit
{
    private FruitCollection<U> baseCollection;

    public FruitCollection(FruitCollection<U> baseCollection)
        : base()
    {
        this.baseCollection = baseCollection;

        // here I added code that throws events whenever the collection is changed
        // I used those events to add/remove fruit to the base collection, and vice versa
        // see the link below.
        ...
    }

    ...
}

public class GenericTree<T>: Tree
    where T : Fruit
{
    private FruitCollection<T> fruits;

    // use the new keyword to hide the base collection
    new public FruiCollection<T> Fruits
    {
        get { return fruits; }
    }

    public GenericTree()
        : base()
    {
        // after hiding the base collection, use base.Fruits to get it
        fruits = new FruitCollection<T, Fruit>(base.Fruits);
    }
}
公共水果收藏:水果收藏
T:U在哪里
哪里有水果
{
私人水果收集基地收集;
公共果品收集(果品收集基地收集)
:base()
{
this.baseCollection=baseCollection;
//在这里,我添加了每当集合发生更改时抛出事件的代码
//我使用这些事件向基本集合添加/删除水果,反之亦然
//请参阅下面的链接。
...
}
...
}
公共类GenericTree:树
T:水果
{
私人水果收藏;
//使用new关键字隐藏基本集合
新公共采摘水果
{
获取{返回水果;}
}
公共GenericTree()
:base()
{
//隐藏基本集合后,使用base.Fruits获取它
果品=新果品系列(基本果品);
}
}

这对我很有帮助:

请注意,通过将树和GenericTree类合并为一个类,可以很容易地解决这一问题。但请记住,这只是一个示例