有没有办法确保一个泛型类型将成为另一个类的基类在C#中?

huangapple go评论84阅读模式
英文:

Is there a way to ensure a generic type will be a base class of another class in C#?

问题

这是我的问题:

public abstract class Animal
{
    protected List<List<???>> _groups;

    public Animal(List<List<???>> groups)
    {
        _groups = groups;

        foreach (List<????> group in groups)
        {
            group.Add(this);    
        }
    }
}

public abstract class Mammal : Animal
{
    public Mammal(List<List<???>> groups) : base(groups)
    {
    }
}

public class Dog : Mammal
{
    public Dog(List<List<???>> groups) : base(groups)
    {
    }
}

正如你所看到的,我有一个Animal抽象类及其子类,如Mammal。这些子类也可能有它们自己的具体子类,如Dog。动物类有一个_groups字段。一组动物可以看起来像这样:

List<Mammal> mammals = new List<Mammal>();

// 或者

List<Animal> twoLegsAnimals = new List<Animal>();

当创建一个动物时,你可以将它添加到它所属的所有组中。问题在于(如你所看到的,使用???作为泛型),如何将动物传递给它所属的组,以便它不能将自己添加到它不属于的组中(例如,Duck不能属于哺乳动物组),同时也能够将自己添加到所有需要的组中。

我尝试过类似这样的做法:

public class Dog : Mammal
{
    public Dog(List<List<T>> groups) : base(groups) where Dog : T
    {
    }
}

从逻辑上讲,这是有意义的,因为你只能将狗添加到它可能属于的组中。问题在于C#不允许我说Dog必须扩展T。基本上,T需要是Dog的基类。有没有办法绕过这个问题?

编辑: "group" 概念存在很多混淆。在这里,我只是指一组动物的列表。例如,鹿和绵羊可以被添加到"猎物"组中。然后,狼可以具有一个Hunt(List<Animal> preys)方法,用于捕杀随机的猎物。它将使用所有动物都可用的Kill方法,从其所在的所有组中移除猎物。我真的看不到实现这种互动的其他方法。

编辑2: 对不起,我发现我之前写错了。最初我写成了:

List<List<Mammal>> mammals = new List<List<Mammal>>();

// 或者

List<List<Animal>> twoLegsAnimals = new List<List<Animal>>();

希望"groups"现在更加清晰。

英文:

So here is my problem:

public abstract class Animal
{
    protected List&lt;List&lt;???&gt;&gt; _groups;

    public Animal(List&lt;List&lt;???&gt;&gt; groups)
    {
        _groups = groups;

        foreach (List&lt;???&gt; group in groups)
        {
            group.Add(this);    
        }
    }
}


public abstract class Mammal : Animal
{
    public Mammal(List&lt;List&lt;???&gt;&gt; groups) : base(groups)
    {
    }
}

public class Dog : Mammal
{
    public Dog(List&lt;List&lt;???&gt;&gt; groups) : base(groups)
    {
    }
}


As you can see, I have an Animal abstract class and its subclasses like Mammal. These subclasses will also have their own, possibly concrete subclasses like Dog. The animal class has a _groups field. A group of animals could look like this:

List&lt;Mammal&gt; mammals = new List&lt;Mammal&gt;();

//         or

List&lt;Animal&gt; twoLegsAnimals = new List&lt;Animal&gt;();

When creating an animal you can pass it all the groups it belongs to. The problem (as you can see by the ??? used as generics) is how do you pass the animal the groups so that it can't add itself into a group it does not belong to (eg. a Duck cannot be in the mammals group) AND so that it can add itself into all groups it needs to.

I tried doing something like this:

public class Dog : Mammal
{
    public Dog(List&lt;List&lt;T&gt;&gt; groups) : base(groups) where Dog : T
    {
    }
}

this would logicaly make sense because you can pass the dog only the groups it could be in. The problem is that C# does not allow me to say that Dog has to extend T. Basically T needs to be a base of Dog. Any way to get around this?

EDIT: Ok there is a lot of confusion going on with the "group" concept. By group I just mean a list of animals. For example a Deers and Sheeps could be added to preys group. A Wolf could then have a Hunt(List&lt;Animal&gt; preys) method that would kill a random prey. He would use a Kill method on the prey (available on all animals) that will remove the prey from all the groups it is in. I don't really see any other way to achieve this kind of interaction.

EDIT2: Sorry I figured out what I wrote wrong. Originaly I wrote:

List&lt;List&lt;Mammal&gt;&gt; mammals = new List&lt;List&lt;Mammal&gt;&gt;();

//         or

List&lt;List&lt;Animal&gt;&gt; twoLegsAnimals = new List&lt;List&lt;Animal&gt;&gt;();

I hope "groups" makes more sense now.

答案1

得分: 1

你可以像这样添加类型约束:

public abstract class Animal<T> where T : Animal<T>
{
    protected List<List<T>> _groups;

    public Animal(List<List<T>> groups)
    {
        _groups = groups;

        if (this is T x)
        {
            foreach (List<T> group in groups)
            {
                group.Add(x);
            }
        }
    }
}

然后,例如 Dog 类将声明为:

public class Dog : Animal<Dog> {  }

当然,你不能阻止用户将 Fish : Animal<Fish> 之类的内容传递给 Dog。你可以在构造函数中进行运行时检查:

public Animal(List<List<T>> groups)
{
    if (typeof(T) != this.GetType()) throw ;
}

附注:你的分解看起来相当笨拙。动物群似乎是跨越特定动物类的概念,因此在个别动物的成员中拥有群组在数据结构设计角度看起来非常不直观。

英文:

You can add a type constraint like this:

public abstract class Animal&lt;T&gt; where T : Animal&lt;T&gt;
{
    protected List&lt;List&lt;T&gt;&gt; _groups;

    public Animal(List&lt;List&lt;T&gt;&gt; groups)
    {
        _groups = groups;

        if (this is T x)
        {
            foreach (List&lt;T&gt; group in groups)
            {
                group.Add(x);
            }
        }
    }
}

then, e.g. the Dog class will be declared as:

public class Dog : Animal&lt;Dog&gt; { … }

As a matter of course, you can't prevent the user passing in e.g. a Fish : Animal&lt;Fish&gt; into Dog. What you could do, is a runtime check in the constructor:

public Animal(List&lt;List&lt;T&gt;&gt; groups)
{
    if (typeof(T) != this.GetType()) throw …;

Side note: Your decomposition looks pretty awkward. Groups of animals seem to be a concept that spans beyond a particular animal class, and so having groups being a member of an individual animal looks very counterintuitive from the data structure design perspective.

huangapple
  • 本文由 发表于 2023年7月27日 20:55:59
  • 转载请务必保留本文链接:https://go.coder-hub.com/76779959.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定