C#函数返回类型为Interface的类型的实例

时间:2014-09-12 21:34:55

标签: c# generics

我有以下内容:

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

我知道类型不同但它们实现了接口。

有任何线索吗?

2 个答案:

答案 0 :(得分:1)

实现此功能的唯一方法是IProvider在其通用属性中是否协变。

public interface IProvider<out Tin, out Tout>
    where Tin : IInput
    where Tout : IOutput
{
}

这样做可以消除您的示例代码的编译错误,但是因为您没有在Tin内显示ToutIProvider所做的事情,所以我无法做到说它是否会给你其他编译器错误。

你必须声明它协变以便能够将它用作参数的原因是,如果不是,你可能会有不好的事情。这是一个简单的例子,说明它可能出错:

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>返回,那么由于CatIAnimal会使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必须实现IOutputTInput必须实现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();
}

Check this working sample in DotNetFiddle