C#通用列表中的数组协方差

时间:2012-01-04 11:34:24

标签: c# generics abstract covariance ilist

我有一个例子,我希望抽象类接口返回类似这样的东西

abstract class AnimalProcessor {
    public abstract IList<Animal> ProcessResults();
}

然后是具体的例子

class GiraffeProcessor : AnimalProcessor {
    public override IList<Animal> ProcessResults() {
        return new List<Giraffe>();
    }
}

class LionProcessor : AnimalProcessor {
    public override IList<Animal> ProcessResults() {
        return new List<Lion>();
    }
}

问题是具体类需要具有相同的签名才能覆盖ProcessResults()方法,因此需要返回IList<Animal>,但是我要返回的ACTUAL数据是{{1 },IList<Lion>等,但调用代码必须执行

IList<Giraffe>

哪个不给我一个我想要的Ilist。

问题

1)以上代码无法编译。 giraffeProcessor必须返回一个具体的GiraffeProcessor processor = new GiraffeProcessor(); IList<Animal> results = processor.GetResults(); ,你可以用List<Animal>个对象填充它,但你要构造的对象类型必须是Giraffe。不理想。

2)当您返回结果时,您只能获得List<Animal>,而不是IList<Animal>。我已经尝试明确地向IList<Giraffe>投射 IList<Giraffe> 这会产生运行时错误,可能是因为返回的对象不是IList<Giraffe> results = (IList<Giraffe>) processor.GetResults();,而是IList<Giraffe> CONTAINS IList<Animal>个对象。

任何人都可以通过我的设计向我提出错误的建议,因为我有点难以实现这一目标。

3 个答案:

答案 0 :(得分:6)

怎么样:

abstract class AnimalProcessor<T> where T : Animal {
    public abstract IList<T> ProcessResults();
}

class GiraffeProcessor : AnimalProcessor<Giraffe> {
    public override IList<Giraffe> ProcessResults() {
        return new List<Giraffe>();
    }
}

class LionProcessor : AnimalProcessor<Lion> {
    public override IList<Lion> ProcessResults() {
        return new List<Lion>();
    }
}

答案 1 :(得分:1)

您可以通过使用泛型类型约束声明AnimalProcessor来解决此问题,例如:

public abstract class AnimalProcessor<T> where T : Animal 
{    
    public abstract IList<T> ProcessResults(); 
} 

如果不起作用,可以使用LINQ Cast运算符,例如:

public class GiraffeProcessor : AnimalProcessor 
{     
    public override IList<Animal> ProcessResults() 
    {         
        return new List<Giraffe>().Cast<Animal>();
    } 
}

或者,将列表内部存储为Animal,但将Giraffe添加到其中,例如

public class GiraffeProcessor : AnimalProcessor 
{     
    private List<Giraffe> _innerList = new List<Giraffe>();
    public override IList<Animal> ProcessResults() 
    {         
        return new List<Animal>(innerList );        } 
}

致以最诚挚的问候,

答案 2 :(得分:1)

如果您使用的是C#4.0,则可以问自己处理器是应该返回IEnumerable<T>而不是IList<T>。如果答案是肯定的,那么你可以从协方差中获益:

abstract class AnimalProcessor { 
    public abstract IEnumerable<Animal> ProcessResults(); 
} 

class GiraffeProcessor : AnimalProcessor { 
    public override IEnumerable<Animal> ProcessResults() { 
        return new List<Giraffe>(); 
    } 
} 

class LionProcessor : AnimalProcessor { 
    public override IEnumerable<Animal> ProcessResults() { 
        return new List<Lion>(); 
    } 
} 

这里有几个优势。首先,您可以将它们实现为迭代器块:

class GiraffeProcessor : AnimalProcessor { 
    public override IEnumerable<Animal> ProcessResults() { 
        yield break;
    } 
} 

其次,不那么简单,您允许客户端代码决定将动物转移到哪种集合中 - 如果有的话。例如,请考虑消费者可能需要LinkedList<Animal>

var animals = new LinkedList<Animal>(animalProcessor.ProcessResults());

或者考虑客户端可能只需要迭代序列:

foreach (var animal in animalProcessor.ProcessResults())
    { /*... do something ...*/ }

在任何一种情况下,如果您在ProcessResults中使用ToList()调用,那么您将无需创建列表。如果消费者真的想要List<Animal>,那么这很容易实现:

var animals = new List<Animal>(animalProcessor.ProcessResults());

最后,即使您更改了方法返回值的接口类型,您也可以从通用方法中受益:

abstract class AnimalProcessor<T> where T : Animal { 
    public abstract IEnumerable<T> ProcessResults(); 
} 

class GiraffeProcessor : AnimalProcessor<Giraffe> { 
    public override IEnumerable<Giraffe> ProcessResults() { 
        yield break;
    } 
} 

class LionProcessor : AnimalProcessor<Lion> { 
    public override IEnumerable<Lion> ProcessResults() { 
        return Enumerable.Empty<Lion>();
    } 
}