具有泛型返回类型的接口列表

时间:2013-12-19 14:12:47

标签: c# generics interface return-type

interface IFly<T>
    {
        T GetMark();
    }

    public class Bird : IFly<string>
    {
        public string GetMark()
        {
            return "Bird";
        }
    }

    public class Plane : IFly<int>
    {
        public int GetMark()
        {
            return 123;
        }
    }

    class Program
    {
        static void Main()
        {
            IFly<string> bird = new Bird();
            IFly<int> plane = new Plane();

            Console.WriteLine(bird.GetMark());
            Console.WriteLine(plane.GetMark());

            Console.ReadKey();
        }
    }

我想替换这个

IFly<string> bird = new Bird();
IFly<int> plane = new Plane();

有这样的事情:

var fly = new List<IFly<T>>

有什么建议吗?

3 个答案:

答案 0 :(得分:3)

这可能有效:

public interface IFlyRoot { }

interface IFly<T> : IFlyRoot
{
    T GetMark();
}

然后你可以制作List<IFlyRoot>

答案 1 :(得分:2)

一般情况下,@ Steve的答案是正确的。但是,根据您的需要,您可以尝试使用类型差异,但它不支持值类型(int不起作用)。

注意接口定义中的out

interface IFly<out T>
{
    T GetMark();
}

然后你可以写:

var list = new List<IFly<object>>();
list.Add(bird);

但它不适用于Plane,其中T是值类型(此处为:int)。此解决方案可能不适合您的确切需求。

要更深入地了解为什么方差不适用于值类型,请参阅Jon SkeetEric Lippert的答案。简而言之,这是因为应该保留引用标识,但是不能使用值类型。值类型总是先装箱,丢失该标识。这就是它不能自动运行的原因。这并不是一个干净利落的方式。您可以尝试的一件事是让Plane类明确地实现IFly<object>

public class Plane : IFly<int>, IFly<object>
{
    public int GetMark()
    {
        return 123;
    }

    object IFly<object>.GetMark()
    {
        return GetMark();
    }
}

并添加到列表中:

list.Add(new Plane());

答案 2 :(得分:0)

由于CLR将IFly和IFly视为不同的类型,因此您需要在两者之间建立更直接的关系,在这种情况下,通过实现IFly接口的非泛型版本。然后,只要集合实现此公共接口,集合就会存储对象,例如:

 class Program
{
    static void Main()
    {
        var flyingThings = new ThingsThatFlyCollection();
        var bird = new Bird();
        var bird2 = new Bird();
        var plane = new Plane();

        flyingThings.Add(bird);
        flyingThings.Add(bird2);
        flyingThings.Add(plane);

        Console.WriteLine(flyingThings.GetItemWithCast<string>(0).GetMark());
        Console.WriteLine(flyingThings.GetItemWithCast<string>(1).GetMark());
        Console.WriteLine(flyingThings.GetItemWithCast<int>(2).GetMark());

        foreach (var item in flyingThings.GetItemsWithCast<int>()) 
        {
            Console.WriteLine(item.GetMark());
        }

        foreach (var item in flyingThings.GetItemsWithCast<string>())
        {
            Console.WriteLine(item.GetMark());
        }

        foreach (var item in flyingThings.GetItemsByType<Bird>())
        {
            Console.WriteLine(item.GetMark());
        }

        Console.ReadKey();
    }
}

public interface IFly
{
    object GetMark();
}

public interface IFly<TMark> : IFly
{
    new TMark GetMark();
}

class Plane : IFly<int>
{
    public int GetMark() { return 123; }
    object IFly.GetMark() { return this.GetMark(); }
}

class Bird : IFly<string>
{
    public string GetMark() { return "Bird"; }
    object IFly.GetMark() { return this.GetMark(); }
}

class ThingsThatFlyCollection : Collection<IFly>
{
    public IFly<TMark> GetItemWithCast<TMark>(int index)
    {
        var f = this[index] as IFly<TMark>;
        if (f == null) { throw new InvalidCastException(); }
        return f;
    }

    public IEnumerable<IFly<TMark>> GetItemsWithCast<TMark>()
    {
        var items = this.Where(p => p is IFly<TMark>).Cast<IFly<TMark>>();
        return items;
    }

    public IEnumerable<TFlyer> GetItemsByType<TFlyer>() where TFlyer : IFly 
    {
        var items = this.Where(p => p.GetType() == typeof(TFlyer)).Cast<TFlyer>();
        return items;
    }
}

请记住,根据交易量的不同,向上转换可能会产生一些性能影响,但对于大多数情况来说可能会忽略不计。