C# 泛型列表中的数组协方差

发布于 2024-12-24 21:57:45 字数 1433 浏览 4 评论 0原文

我有一个示例,我希望抽象类接口返回类似这样的内容

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

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

Which 不会给我一个我想要的 Ilist 。

问题

1) 上面的代码无法编译。 giraffeProcessor 必须返回一个具体的 List,您可以使用 Giraffe 对象填充它,但您构造的要返回的对象类型必须是 List;。不理想。

2)返回结果时,只能得到IList,不能得到IList。我尝试显式转换为 IList IList<长颈鹿>结果 = (IList<长颈鹿>) 处理器.GetResults(); 这会产生运行时错误,大概是因为返回的对象不是 IList,而是一个包含 GiraffeIList对象。

任何人都可以建议我的设计在这里做错了什么,因为我对实现这一目标的最佳方法感到有点困惑。

I have an example where I want an abstract class interface to return something like this

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

Then the concrete examples

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

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

The problem is that the concrete classes need to have the same signature to override the ProcessResults() method so they need to return an IList<Animal>, however the ACTUAL data I want to return is an IList<Lion>, IList<Giraffe> etc, but then the calling code has to do

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

Which does not give me an Ilist which is what I want.

Problems

1) Above code does not compile. The giraffeProcessor has to return a concrete List<Animal>, you can populate it with Giraffe objects but the object type you construct to return has to be List<Animal>. Not ideal.

2) When you return the results, you can only get an IList<Animal>, not IList<Giraffe>. I have tried casting explicitly to IList<Giraffe> with
IList<Giraffe> results = (IList<Giraffe>) processor.GetResults();
which gives a runtime error, presumably because the object returned is NOT an IList<Giraffe>, it is an IList<Animal> which CONTAINS Giraffe objects.

Can anyone suggest what I am doing wrong here with my design as Im a bit stumped as to the best way to accomplish this.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

情痴 2024-12-31 21:57:45

怎么样:

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

How about:

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>();
    }
}
饭团 2024-12-31 21:57:45

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

最好的问候,

You could resolve this by declaring AnimalProcessor with a generic type constraint, e.g.

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

If that doesnt work, you could use the LINQ Cast operator, for example:

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

Or, store the list internally as Animal but add Giraffe's to it, e.g.

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

Best regards,

倾听心声的旋律 2024-12-31 21:57:45

如果您使用的是 C# 4.0,您可以询问自己处理器是否应该返回 IEnumerable 而不是 IList。如果答案是“是”,那么您可以从协方差中受益:

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

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

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

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

无论哪种情况,如果您使用 ToList() 在 ProcessResults 中调用,您将毫无意义地创建一个列表。如果消费者确实想要一个 List,这可以很容易地完成:

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

If you are using C# 4.0, you can ask yourself whether the processor should return IEnumerable<T> rather than IList<T>. If the answer is "yes", then you can profit from covariance:

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

You have a couple of advantages here. First, you could implement these as iterator blocks:

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

Second, and less trivially, you allow the client code to decide what kind of collection to dump the animals into -- if any. For example, consider that the consumer might want a LinkedList<Animal>:

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

Or consider that the client might need only to iterate the sequence:

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

In either case, if you were using a ToList() call in ProcessResults, you'd be creating a list for nothing. If the consumer really wants a List<Animal>, that can be accomplished very easily:

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

Finally, you can also benefit from the generic approach, even if you change the interface type of the method's return value:

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>();
    } 
} 
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文