我试图在他们创建的疯狂情况下向某人展示接口的用途。它们在列表中有几个不相关的对象,需要对每个对象中的两个字符串属性执行操作。我指出,如果他们将属性定义为接口的一部分,他们可以使用接口对象作为作用于它的方法参数的类型;例如:
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<T>
成员执行此操作,但我一直在寻找可能在.NET 2.0中运行的方法。看来这应该是可能的;我错过了什么?
答案 0 :(得分:5)
问题在于方法,而不是它的方式.....
void PrintProperties<SP>(IEnumerable<SP> list) where SP: ISpecialProperties
{
foreach (var item in list)
{
Console.WriteLine("{0} {1}", item.Prop1, item.Prop2);
}
}
答案 1 :(得分:5)
失败的原因是因为泛型不会在C#中表现出差异。
关于IEnumerable&lt; T&gt;的修复,请尝试以下方法:
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
}
}
编辑:对于这种特殊情况,其他现有答案优于上述答案,但如果你真的需要“转换”序列,我会把它留在这里作为一个普遍有用的东西。
答案 2 :(得分:1)
您可以在列表中使用foreach
。 foreach
执行内置演员。因此,如果您从函数中取出循环,您可以编写类似
List<Test> myList = new List<Test>();
for (int i = 0; i < 5; i++)
{
myList.Add(new Test());
}
foreach (IDoSomething item in myList)
{
item.DoSomething();
}
答案 3 :(得分:1)
您想要的是“接口协方差”,目前C#不支持。您可以在Eric Lippert's blog上阅读相关内容。
答案 4 :(得分:-1)
这不能回答你的问题(或者我猜想的练习点:),但在这种情况下,我只是通过将特殊属性附加到感兴趣的属性来使用反射。