繁体   English   中英

具有泛型类型的接口的C#函数返回实例

[英]C# Function return instance of type Interface with Generic Types

我有以下内容:

    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
{
}

这样做可以消除示例代码的编译器错误,但是由于您没有在IProvider显示对TinTout因此无法确定它是否会给您带来其他编译器错误。

必须将其声明为协变变量才能将其用作参数的原因是,如果没有,则可能会导致不良后果。 这是一个简化的示例,说明它可能会出错:

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> ,从而由于其为IAnimalAddAnimal而使添加Cat完全合法,则它将为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方法,这也不允许您将猫添加到集合中。

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 / TInputProvider给工厂方法本身的参数相同

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中检查此工作示例

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM