【问题标题】:Is there a way to cast generic lists to lists of interface/base class types?有没有办法将通用列表转换为接口/基类类型列表?
【发布时间】:2008-10-09 19:17:57
【问题描述】:

我试图向某人展示在他们创造的疯狂情况下使用接口。它们在列表中有几个不相关的对象,并且需要对每个对象中的两个字符串属性执行操作。我要指出的是,如果他们将属性定义为接口的一部分,他们可以使用接口对象作为作用于它的方法参数的类型;例如:

void PrintProperties(IEnumerable<ISpecialProperties> list)
{
    foreach (var item in list)
    {
        Console.WriteLine("{0} {1}", item.Prop1, item.Prop2);
    }
}

这看起来一切都很好,但是需要处理的列表没有(也不应该)用接口作为类型参数声明。但是,您似乎无法转换为不同的类型参数。例如,这失败了,我不明白为什么:

using System;
using System.Collections.Generic;

namespace ConsoleApplication2
{
    class Program
    {
        static void Main(string[] args)
        {
            List<Test> myList = new List<Test>();
            for (int i = 0; i < 5; i++)
            {
                myList.Add(new Test());
            }

            PrintList((IEnumerable<IDoSomething>)myList);
        }

        static void PrintList(IEnumerable<IDoSomething> list)
        {
            foreach (IDoSomething item in list)
            {
                item.DoSomething();
            }
        }
    }

    interface IDoSomething
    {
        void DoSomething();
    }

    public class Test : IDoSomething
    {
        public void DoSomething()
        {
            Console.WriteLine("Test did it!");
        }
    }
}

可以使用Enumerable.Cast&lt;T&gt; 成员来执行此操作,但我正在寻找一种可能也适用于.NET 2.0 的方法。似乎这应该是可能的;我错过了什么?

【问题讨论】:

    标签: .net generics covariance


    【解决方案1】:

    问题在于方法,而不是它的调用方式.....

    void PrintProperties<SP>(IEnumerable<SP> list) where SP: ISpecialProperties
    {
        foreach (var item in list)
        {
            Console.WriteLine("{0} {1}", item.Prop1, item.Prop2);
        }
    }
    

    【讨论】:

    • 正是我刚刚写的。
    • 我偶然发现了它,并打算将其发布。这有点奇怪,但我能理解为什么会这样;你必须指定类型是可转换的。
    【解决方案2】:

    它失败的原因是泛型在 C# 中还没有表现出差异。

    但是,对于 IEnumerable 的修复,试试这个:

    public static IEnumerable<TBase> SafeConvert<TBase, TDerived>(IEnumerable<TDerived> source)
        where TDerived : TBase
    {
        foreach (TDerived element in source)
        {
            yield return element; // Implicit conversion to TBase
        }
    }
    

    编辑:对于这种特殊情况,其他现有答案比上述更好,但如果您确实需要“转换”序列,我将把它留在这里作为一般有用的东西。

    【讨论】:

    • Ah Mr. Skeet - 非常好的例子,对我帮助很大,谢谢!
    【解决方案3】:

    您可以在您拥有的列表中使用foreachforeach 做了一个内置的演员表。因此,如果您将循环从函数中取出,您可以编写类似

    List<Test> myList = new List<Test>();
    for (int i = 0; i < 5; i++)
    {
       myList.Add(new Test());
    }
    
    foreach (IDoSomething item in myList)
    {
       item.DoSomething();
    }
    

    【讨论】:

    • 需要注意的一件好事(foreach 中的强制转换),但没有解决将各种派生类型的列表传递给同一函数的问题,以防函数必须执行其他操作。
    【解决方案4】:

    您想要的称为“接口协方差”,目前 C# 不支持。您可以在Eric Lippert's blog 上阅读。

    【讨论】:

      【解决方案5】:

      这并不能回答您的问题(或者我猜是练习的重点:),但在这种情况下,我只是通过将特殊属性附加到感兴趣的属性来使用反射。

      【讨论】:

      • 哎呀!那是缓慢而复杂的。我宁愿只是告诉我的朋友复制代码以因类型而异。
      猜你喜欢
      • 1970-01-01
      • 2015-05-26
      • 1970-01-01
      • 2016-02-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-11-05
      • 1970-01-01
      相关资源
      最近更新 更多