2012-01-04 65 views
4

我有我想要一个抽象类,接口,这样阵列协方差在C#泛型列表

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数据是IList<Lion>,IList<Giraffe>等,但是那么调用代码必须做

GiraffeProcessor processor = new GiraffeProcessor(); 
IList<Animal> results = processor.GetResults(); 

哪个不给我一个Ilist,这是我想要的。

问题

1)上面的代码不能编译。 giraffeProcessor必须返回一个具体的List<Animal>,你可以用Giraffe对象填充它,但是你构造返回的对象类型必须是List<Animal>。不理想。

2)当您返回结果时,您只能得到IList<Animal>,而不是IList<Giraffe>。我试图明确地IList<Giraffe>IList<Giraffe> results = (IList<Giraffe>) processor.GetResults(); 它给出了一个运行时错误,大概是因为返回的对象不是IList<Giraffe>,它是一个IList<Animal>其中包含Giraffe对象。

任何人都可以建议我做错了这里与我的设计,因为我有点难住最好的方式来完成这一点。

+1

有一个空白的IList服务,有一个类型的IList不服务的目的是什么? – 2012-01-04 11:39:58

回答

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>(); 
    } 
} 
+0

完美!只是我正在寻找的解决方案,并没有意识到继承父类的声明以类似的泛型传递。非常感谢 – NZJames 2012-01-04 14:58:36

1

您也可以用一个泛型类型约束,例如宣布AnimalProcessor解决此

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

如果不工作,你可以使用LINQ转换运算,例如:

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

或者存储列表内的动物,但长颈鹿添加到它,例如

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

最好的问候,

+1

标准'Cast ()'方法将返回一个'IEnumerable ',而不是'IList ' – 2012-01-04 11:48:00

+0

是的,非常真实。调用.Cast ().ToList()效率不高。我喜欢首先使用泛型类型的基类,或者第二次将innerlist作为不同的类型存储。问候, – 2012-01-04 13:36:38

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 ...*/ } 

在这两种情况下,如果你使用的是ToList()通话在ProcessResults中,你会创建一个无用的列表。如果消费者真的想要一个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>(); 
    } 
} 
+0

谢谢。只是一个关于列表的问题,我很喜欢使用List <>。ForEach(a => action())方法,但似乎每次我得到一个IEnumerable时,我必须先做.ToList ()之前做.ForEach )。我使用这个错误吗? – NZJames 2012-01-05 13:47:39

+0

是的。您的ToList()调用会分配一个数组并将所有项目复制到其中,这是不必要的。你可以简单地实现你自己的扩展方法来允许'IEnumerable '这个语法,但是正如Eric Lippert写的那样,它在哲学上是有问题的。请参阅http://blogs.msdn.com/b/ericlippert/archive/2009/05/18/foreach-vs-foreach.aspx。 (在回复对这篇文章的评论时,他指出,他也受到了列表.ForEach()')的哲学困扰。) – phoog 2012-01-05 14:58:31