簡體   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