Avoid passing generic type parameter of class extending generic class

♀尐吖头ヾ 提交于 2019-12-11 00:04:27

问题


I have an abstract class Creature that takes a generic type argument, extended by two other classes Human and Spider. Each subclass defines the generic type of its parent.

I am stuck with how to pass a subclass as a reference of parent class to a method.

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)
    {

    }
}

This doesnt throw error if PrintList took 2 generic arguments

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

    }

But then I don't want to pass U again, as T was already constructed with U as type parameter, e.g Spider already defined Creature to take type parameter of IStabAble.

So basically, I'm stuck with how to write the method such that it caters both Spider and Human with minimal number of generic parameters.
Thanks


回答1:


I am assuming that PrintList only requires read-only forward-only access to the list.

The solution is to make the PrintList method accept an IEnumerable<Creature<T>> like this:

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

And call it like this:

PrintList(spiderList);

Because the generic type parameter T in IEnumerable<T> is covariant, this will work.

In your special case, because you are using .NET 2.0 (which does not support covariant type parameters) this solution will not work. Here is a workaround:

Create a Cast method that can convert between enumerables with different item types like this (in .NET 3.5, we already have such method as an extension method):

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

And use it like this:

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


来源:https://stackoverflow.com/questions/37296664/avoid-passing-generic-type-parameter-of-class-extending-generic-class

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!