【问题标题】:Avoid passing generic type parameter of class extending generic class避免传递扩展泛型类的类的泛型类型参数
【发布时间】:2016-09-14 18:23:14
【问题描述】:

我有一个抽象类 Creature,它接受一个泛型类型参数,由另外两个类 Human 和 Spider 扩展。每个子类都定义了其父类的泛型。

我不知道如何将子类作为父类的引用传递给方法。

public interface IDamagable
{
    void OnSimpleHit();
}

public interface IStabAble : IDamagable
{
    void OnKnifeStab();
}

public interface ISlapAble : IDamagable
{
    void OnSlap();
}

public abstract class Creature<T> where T : IDamagable
{
    public abstract void Init(T damageListener);
}

public abstract class Human : Creature<ISlapAble>
{

}

public abstract class Spider : Creature<IStabAble>
{

}

public class MainClass
{
    public void Test()
    {
        List<Spider> spiderList = new List<Spider>();
        List<Human> humanList = new List<Human>();

        PrintList<IDamagable>(spiderList); // Argument `#1' cannot convert
        //`System.Collections.Generic.List<newAd.B_A_A>' expression 
        //to type `System.Collections.Generic.List<newAd.A_A<newAd.I_B>>'
    }

    protected void PrintList<T>(List<Creature<T>> list)
    {

    }
}

如果 PrintList 采用 2 个通用参数,这不会引发错误

protected void PrintList<T,U>(List<T> list) where T : Creature<U> where U : IDamagable
    {

    }

但是我不想再次传递 U,因为 T 已经用 U 作为类型参数构造了,例如 Spider 已经定义了 Creature 来获取 IStabAble 的类型参数。

所以基本上,我一直坚持如何编写方法,以便以最少的泛型参数同时满足 Spider 和 Human 的需求。
谢谢

【问题讨论】:

  • 您应该考虑将您的类重命名为易于理解的名称,从而使您的类层次结构中的关系易于遵循。如果仅仅理解您的代码肯定会让人头疼,那么您将不会得到太多帮助。
  • 尝试将PrintList方法的签名改为void PrintList&lt;T&gt;(IEnumerable&lt;A_A&lt;T&gt;&gt; list) where T : I_A
  • 重命名了类和接口等 @YacoubMassad 我不明白将列表更改为 IEnumerable 有何改变
  • 能否将文本中的所有内容重命名为?同样在注释代码中
  • @Farhan,与List&lt;T&gt;不同,IEnumerable&lt;T&gt;中的泛型类型参数是covariant

标签: c# generics inheritance parameter-passing


【解决方案1】:

我假设PrintList 只需要对列表进行只读只进访问。

解决方案是让PrintList 方法接受IEnumerable&lt;Creature&lt;T&gt;&gt;,如下所示:

void PrintList<T>(IEnumerable<Creature<T>> list) where T: IDamagable
{
    //...
}

然后这样称呼它:

PrintList(spiderList);

因为IEnumerable&lt;T&gt; 中的泛型类型参数Tcovariant,所以这将起作用。

在您的特殊情况下,因为您使用的是 .NET 2.0(不支持协变类型参数),所以此解决方案将不起作用。这是一个解决方法:

创建一个Cast 方法,可以像这样在具有不同项目类型的枚举之间进行转换(在.NET 3.5 中,我们已经有了这样的方法作为扩展方法):

public static IEnumerable<U> Cast<T, U>(IEnumerable<T> source) where T : U
{
    foreach (var item in source)
    {
        yield return item;
    }
}

并像这样使用它:

PrintList(Cast<Spider, Creature<IStabAble>>(spiderList));

【讨论】:

  • 所以我必须使用的泛型类型的最小数量是 2。我确实打算使用随机列表索引,但是,为了澄清最后一点,我提出的变体 protected void PrintList&lt;T,U&gt;(List&lt;T&gt; list) where T : Creature&lt;U&gt; where U : IDamagable { } 也是在效率方面同样好吗?不需要类型转换所以...?
  • 我的PrintList 版本只有一个类型参数。但是在调用它之前,您必须将可枚举类型转换为正确的类型。您的版本效率更高一些,因为它不涉及强制转换。如果您需要按索引访问项目,那么您可以使用List,因此您需要有两个参数。
  • 非常感谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-03-05
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多