繁体   English   中英

C#泛型继承问题

[英]C# Generics Inheritance Problem

我想将从泛型中派生的不同类型的对象添加到基类型列表中。 我得到这个编译错误

Error   2   Argument 1: cannot convert from 'ConsoleApplication1.Stable' to 'ConsoleApplication1.ShelterBase<ConsoleApplication1.AnimalBase>'   C:\Users\ysn\Desktop\ConsoleApplication1\ConsoleApplication1\Program.cs 43  26  ConsoleApplication1

我看不出问题你能为我提供另一种做这种事情的方法吗?

abstract class AnimalBase { public int SomeCommonProperty;}

abstract class ShelterBase<T> where T : AnimalBase
{
    public abstract List<T> GetAnimals();
    public abstract void FeedAnimals(List<T> animals);
}


class Horse : AnimalBase { }

class Stable : ShelterBase<Horse>
{
    public override List<Horse> GetAnimals()
    {
        return new List<Horse>();
    }

    public override void FeedAnimals(List<Horse> animals)
    {
        // feed them
    }
}


class Duck : AnimalBase { }

class HenHouse : ShelterBase<Duck>
{
    public override List<Duck> GetAnimals()
    {
        return new List<Duck>();
    }

    public override void FeedAnimals(List<Duck> animals)
    {
        // feed them
    }
}

class Program
{
    static void Main(string[] args)
    {
        List<ShelterBase<AnimalBase>> shelters = new List<ShelterBase<AnimalBase>>();

        ///////////////////////////// following two lines do not compile
        shelters.Add(new Stable()); 
        shelters.Add(new HenHouse());
        /////////////////////////////

        foreach (var shelter in shelters)
        {
            var animals = shelter.GetAnimals();
            // do sth with 'animals' collection
        }
    }
}

您可以使用逆变 ,但只有当你改变你的抽象类的接口和返回类型GetAnimalsIEnumerable<T>因为List<T>不支持此功能。

有效的代码:

abstract class AnimalBase { public int SomeCommonProperty;}

interface IShelterBase<out T> where T : AnimalBase
{
    IEnumerable<T> GetAnimals();
}

class Horse : AnimalBase { }

class Stable : IShelterBase<Horse>
{
    public IEnumerable<Horse> GetAnimals()
    {
        return new List<Horse>();
    }
}

class Duck : AnimalBase { }

class HenHouse : IShelterBase<Duck>
{
    public IEnumerable<Duck> GetAnimals()
    {
        return new List<Duck>();
    }
}

void Main()
{
    List<IShelterBase<AnimalBase>> shelters = new List<IShelterBase<AnimalBase>>();

    shelters.Add(new Stable());
    shelters.Add(new HenHouse());

    foreach (var shelter in shelters)
    {
        var animals = shelter.GetAnimals();
        // do something with 'animals' collection
    }
}

您可以使用协方差和逆变来完成此操作,但您的ShelterBase类需要从接口派生,因为只有接口可以是共变或逆变。 你的列表需要是一个List<IShelterBase<T>> ,它应该可以工作。

有关详细信息,请参见此处

关于泛型中的co和contra-variance的文章:

http://msdn.microsoft.com/en-us/library/dd799517.aspx

要解决这个特殊问题,您实际上并不需要协方差。 当您使用动物列表时,仍然可以通过IShelterBase<out T>界面获取AnimalBase 不妨通过基类公开AnimalBase列表。

一个更好的和更清洁的设计是使GetAnimals返回列表AnimalBase ,并建立在每个避难所类的重载返回特定动物的名单。

abstract class ShelterBase<T> where T : AnimalBase
{
    public List<AnimalBase> GetAnimals(){return new List<AnimalBase>();}
}

class Stable : ShelterBase<Horse>
{
    public List<Horse> GetHorses(){return new List<Horse>();}
}

此设计的另一个问题是通过最大派生类型(即List ,而不是IEnumerableIList )公开集合。 看到你在创建一个抽象动物集合的类时遇到麻烦,你应该通过禁止直接插入/删除功能来真正保护内部集合。 一旦你这样做,事情变得容易一些。 例如,我将如何解决这个问题。

abstract class ShelterBase<T> where T : AnimalBase
{
    protected List<T> _Animals;
    public AnimalBase() {
       _Animals = CreateAnimalCollection();
    }

    protected abstract List<T> CreateAnimalCollection();
    public IEnumerable<AnimalBase> GetAnimals(){return _Animals.Cast<AnimalBase>();}

    //Add remove operations go here
    public void Add(T animal){_Animals.Add(animal);}
    public void Remove(T animal){_Animals.Remove(animal);}

}

class Stable : ShelterBase<Horse>
{
    protected override List<Horse> CreateAnimalCollection(){return new List<Horse>();}

    public IEnumerable<Horse> GetHorses(){return _Animals;}
}

您会注意到动物的内部集合从未暴露为可变列表。 这很好,因为它可以让你更严格地控​​制其内容。 在这个示例中,基本掩体中的AddRemove方法有点人为,因为它们不会在直接集合访问中添加任何额外的东西,但您可以在那里添加逻辑 - 例如,在添加或检查动物年龄之前检查最大的遮蔽大小删除它。

我刚遇到类似的问题。 我的所有实体都派生自一个基类,我创建了一个方法来返回一个连接的id列表。 因此使用泛型创建了方法并获得了一些转换错误。 管理将通用T转换Object和Object to BaseClass并进行某种验证以防万一。

//Needs it generic to use in a lot of derived classes
private String ConcatIds<T>(List<T> listObj)
{
    String ids = String.Empty;

    foreach (T obj in listObj)
    {
        BaseEntity be = CastBack(obj);

        if (ids.Count() > 0)
            ids = ids + String.Format(", {0}", be.Id);
        else
            ids = be.Id.ToString();
    }

    return ids;
}

//I'll probably move it to the Base Class itself
private BaseEntity CastBack<T>(T genericObj)
{
    Type type = typeof(T);

    if (type.BaseType == typeof(BaseEntity))
    {
        Object _obj = (Object)genericObj;
        return (BaseEntity)_obj;
    }
    else
    {
        throw new InvalidOperationException(String.Format("Cannot convert {0} to BaseEntity", type.ToString()));
    }
}

用法:

public class BaseEntity
{
   public Int32 Id {get; set;}
}

public class AnyDerivedClass : BaseEntity
{
  // Lorem Ipsum
}

private void DoAnything(List<AnyDerivedClass> myList)
{
    String ids = this.ConcatIds<AnyDerivedClass>(myList);
}

编辑:过了一段时间我需要创建更多的层次结构级别,有时会转回父级或祖父级。 所以我让我的CastBack方法更通用。

private B CastBack<T,B>(T genericObj)
{
    Type type = typeof(T);

    if (type.BaseType == typeof(B))
    {
        Object _obj = (Object)genericObj;
        return (B)_obj;
    }
    else
    {
        throw new InvalidOperationException(String.Format("Cannot cast back {0}", type.ToString()));
    }
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM