5

如何使列表包含通用接口的所有不同实现?

例如

public class Animal { // some Animal implementations }
public class Dog : Animal { // some Dog implementations }
public class Snake : Animal { // some Snake implementations }

public interface ICatcher<T> where T: Animal
{
    // different animals can be caught different ways.
    string Catch(T animal);
}

public class DogCatcher : ICatcher<Dog> 
{ 
    string Catch(Dog animal) { // implementation }
}

public class SnakeCatcher : ICatcher<Snake> 
{ 
    string Catch(Snake animal) { // implementation }
}

我想把所有的捕手放在一个类似的列表中,

public class AnimalCatcher
{
     // this will hold the catching method an animal catcher knows (just something similar)
     public IEnumerable<ICatcher<Animal>> AnimalCatcher = new List<ICatcher<Animal>>
     {
          new DogCatcher(),
          new SnakeCatcher()
     }
}

我知道这是处理 c# 中的泛型修饰符(协变、逆变和不变性)但无法让它工作的东西。

尝试:在中添加“out”

public interface ICatcher<out T> where T: Animal
{
    // different animals can be caught different ways.
    string Catch(T animal);
}

但给出编译时错误:

“类型参数 'T' 必须在 'ICatcher.Catch(T)' 上逆变有效。'T' 是协变的。”

我究竟做错了什么?

4

4 回答 4

2

您需要对动物进行一些类型统一,并且需要删除的泛型声明ICatcher并使其成为具体类型:

public interface IAnimal {}

public class Dog : IAnimal {}
public class Snake : IAnimal {}

public interface ICatcher
{
    // different animals can be caught different ways.
    string Catch(IAnimal animal);
}

然后你可以有一个像这样的捕手集合:

public class AnimalCatcher
{
     // this will hold the catching method an animal catcher knows (just something similar)
     public IEnumerable<ICatcher> AnimalCatcher = new List<ICatcher>
     {
          new DogCatcher(),
          new SnakeCatcher()
     }
}

ETA:这是一个repl.it演示如何使用接口进行设置。虽然,在各自的Catch实现中,您将不得不强制转换IAnimal接口(如果您需要访问特定于特定动物实现的实例变量)

于 2019-11-11T14:43:52.557 回答
2

如果DogAnimal,并不意味着ICatcher<Dog>就是ICatcher<Animal>ICatcher<Dog>和之间没有任何关系ICatcher<Animal>

微软做出这个决定是为了防止在逆变的情况下出现运行时错误T。让我们来看看:

ICatcher<Dog> dogCatcher = new DogCatcher();
ICatcher<Animal> animalCatcher = dogCatcher; 

// Then, we can pass `Snake` as input to the `animalCatcher` which is actually `DogCatcher`
animalCatcher.Catch(new Snake()); // ???

就是说ICatcher<Dog>ICatcher<Animal>我们必须out在前面使用关键字,T它会说ICatcher<T>在 T 中是协变的。这意味着T只会在你的接口中用作返回类型。但是,目前您将T作为输入传递给该Catch方法。

于 2019-11-11T15:17:31.453 回答
0

考虑引入一个ICatcher接口。然后在你的ICatcher<T>.

最后创建一个IEnumerable<ICatcher>代替IEnumerable<ICatcher<Animal>>

Microsoft 库中的泛型具有相同的方法。

于 2019-11-11T15:29:13.133 回答
0

由于您的问题没有说明 List 的用途,因此我对您要解决的问题进行了疯狂的猜测。

我希望你需要的是

public interface ICatcher
{
     void MethodThatMakesYouListUseful()
}

public interface IPerAnimalCatcher<T> : ICatcher where T: Animal
{
    // different animals can be caught different ways.
    string Catch(T animal);
}

AnimalCatchers = new List<ICatcher>();

因为除非您自己定义,否则泛型接口之间没有有用的类型关系。

于 2019-11-11T15:25:28.657 回答