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>,但是我想要返回的实际数据是IList<Lion>IList<Giraffe>等,但是调用代码必须执行

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

这并没有给我一个伊利斯特,这正是我想要的。

问题

1) 以上代码不编译。长颈鹿处理器必须返回一个具体的List<Animal>,您可以用Giraffe对象填充它,但要返回的对象类型必须是List<Animal>。不理想。

2) 返回结果时,只能得到IList<Animal>,而不能得到IList<Giraffe>。我已尝试使用显式转换为IList<Giraffe>IList<Giraffe> results = (IList<Giraffe>) processor.GetResults();这给出了一个运行时错误,可能是因为返回的对象不是IList<Giraffe>,而是包含Giraffe对象的IList<Animal>

有人能建议我在设计上做错了什么吗?因为我有点困惑于实现这一点的最佳方式。

怎么样:

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

您可以通过使用泛型类型约束(例如)声明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 );        } 
}

致问候,

如果您使用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>();
    } 
} 

相关内容

  • 没有找到相关文章

最新更新