我有以下内容:
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();
}
我知道类型不同但它们实现了接口。
有任何线索吗?
答案 0 :(得分:1)
实现此功能的唯一方法是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
方法非法,但现在可以非常安全地说#34;这些狗的集合被表示为动物的集合&#34;因为现在没有办法在集合中添加一只不是狗的动物。
要使上述代码编译,您必须进行两次权衡之一。让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
方法,这也不允许您将猫添加到集合中。
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();
}
}
答案 1 :(得分:1)
我将冒险向您解释替代方法。
也许我错了。看起来您正在寻找使用其他地方的工厂方法创建提供程序实例。
如果你这样做,你就无法避免在这里进行明确的演员:
private static IProvider<IInput, IOutput> GetProvider()
{
// Explicit upcast
return (IProvider<IInput, IOuput>)new Provider();
}
一些建议:通用参数的通用命名方案是大写T
和像TOutput
这样的基于pascal的标识符。
你需要这个演员表,因为C#编译器知道TOutput
必须实现IOutput
,TInput
必须实现IInput
,这要归功于通用约束,甚至Provider
}给出了完全填充整个约束的泛型参数,当工厂方法不能隐式证明TOutput
类的通用参数TInput
/ Provider
是与工厂方法本身相同:
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();
}