C# 函数返回类型为“具有泛型类型的接口”类型的实例
本文关键字:接口 类型 实例 泛型类型 返回类型 函数 | 更新日期: 2023-09-27 18:21:45
>我有以下内容:
public interface IInput
{
}
public interface IOutput
{
}
public interface IProvider<Tin, Tout>
where Tin : IInput
where Tout : IOutput
{
}
public class Input : IInput
{
}
public class Output : IOutput
{
}
public class Provider : IProvider<Input, Output>
{
}
我希望能够获得以下方法来转换结果:
private static IProvider<IInput, IOutput> GetProvider()
{
return new Provider();
}
我知道类型不同,但它们实现了接口。
有什么线索吗?
使其工作的唯一方法是IProvider
在其泛型属性中是协变的。
public interface IProvider<out Tin, out Tout>
where Tin : IInput
where Tout : IOutput
{
}
这样做可以消除示例代码的编译器错误,但是由于您没有显示Tin
和Tout
在IProvider
中正在做什么,所以我不能说它是否会给您带来其他编译器错误。
您必须声明它协变才能将其用作参数的原因是,如果不是这样,您可能会遇到坏事。下面是一个简化的示例,以显示它是如何出错的:
public interface IAnimal { }
public class Dog : IAnimal { }
public class Cat : IAnimal { }
public interface IAnimalCollection<TAnimal> where TAnimal : IAnimal
{
TAnimal GetAnimal(int i);
int AddAnimal(TAnimal animal);
}
public class DogCollection : IAnimalCollection<Dog>
{
private List<Dog> _dogs = new List<Dog>();
public Dog GetAnimal(int i)
{
return _dogs[i];
}
public int AddAnimal(Dog animal)
{
_dogs.Add(animal);
return _dogs.Count - 1;
}
}
class Program
{
static void Main(string[] args)
{
IAnimalCollection<IAnimal> animalCollection = GetDogCollection();
animalCollection.AddAnimal(new Cat()); //We just added a cat to a collection of dogs.
}
private static IAnimalCollection<IAnimal> GetDogCollection()
{
return new DogCollection();
}
}
如果我们允许将DogCollection
作为IAnimalCollection<IAnimal>
返回,这使得添加Cat
完全合法,因为它是IAnimal
,AddAnimal
将是int AddAnimal(IAnimal)
。
通过添加out
这使得AddAnimal
方法非法,但现在说"这个狗的集合被表示为动物的集合"是完全安全的,因为现在没有办法将不是狗的动物添加到集合中。
若要编译上述代码,必须进行以下两个权衡之一。要么不返回接口GetDogCollection()
删除 cat 的添加。
class Program
{
static void Main(string[] args)
{
IAnimalCollection<IAnimal> animalCollection = GetDogCollection();
//animalCollection.AddAnimal(new Cat()); //Would get a compiler error if we tried to add a cat to the collection
}
private static IAnimalCollection<Dog> GetDogCollection()
{
return new DogCollection();
}
}
或者使接口协变,但要做到这一点,您需要从接口中删除 AddAnimal
方法,这也不允许您将 cat 添加到集合中。
public interface IAnimalCollection<out TAnimal> where TAnimal : IAnimal
{
TAnimal GetAnimal(int i);
//int AddAnimal(TAnimal animal); //Can't have methods that take in the type when using "out"
}
class Program
{
static void Main(string[] args)
{
IAnimalCollection<IAnimal> animalCollection = GetDogCollection();
//animalCollection.AddAnimal(new Cat()); //Would get a compiler error because this method no longer exists.
}
private static IAnimalCollection<IAnimal> GetDogCollection()
{
return new DogCollection();
}
}
冒着风险向您解释另一种方法。
也许我错了。您似乎希望使用其他地方的工厂方法创建提供程序实例。
如果你这样做,你无法避免这里的显式强制转换:
private static IProvider<IInput, IOutput> GetProvider()
{
// Explicit upcast
return (IProvider<IInput, IOuput>)new Provider();
}
一些建议:泛型参数的常见命名方案是大写T
和帕斯卡大小写标识符,如TOutput
。
你需要这个强制转换,因为 C# 编译器知道TOutput
必须实现IOutput
,并且由于泛型约束,TInput
必须实现IInput
,即使Provider
给出了完全填充整个约束的泛型参数,当工厂方法无法隐式证明Provider
类的泛型参数 TOutput
/TInput
与提供给工厂方法本身的泛型参数相同时,问题就来了:
public static IProvider<TInput, TOutput> GetProvider<TInput, TOutput>()
where TInput : IInput
where TOutput : IOutput
{
// Hey!!!!!!!! Do TOutput and TInput of this method are the same as
// Provider TOutput and TInput? Who knows, thus, compiler error:
// Cannot implicitly convert type 'Provider' to 'IProvider<TInput,TOutput>'.
// An explicit conversion exists (are you missing a cast?)
return new Provider();
}
在 DotNetFiddle 中检查此工作示例。