2013-03-01 57 views
9

在我的项目中,我有一个MyClass,它实现了IMyClass。我需要通过转换其他项目列表来返回IMyClass的列表。为了简单起见,假设我可以创建一个MyClass,只需将另一个项目传递给它的构造函数即new MyClass(item)即可。我应该投我的lambda或投IEnumerable?

考虑以下两行,其中(据我所知)产生相同的结果:

var option1 = items.Select(item => new MyClass(item)).Cast<IMyClass>().ToList() 
var option2 = items.Select(item => new MyClass(item) as IMyClass).ToList() 

这在我看来,选项1将需要双重枚举,一旦投中的所有项目到我的界面和一次生成列表。如果我是对的,那么选项#2会更聪明。然而,我已经从来没有看到使用类似选项#2的任何代码,并且我倾向于认为我不够聪明,想出其他C#社区没有的聪明之处。

在附注中,我认为选项#2更美观,但这只是我。

我的问题是:是我的选择#2一个更好的想法,就像我认为它是?是否有任何遗漏或其他原因,为什么我想坚持选项#1?或者我可能比较两个愚蠢的想法,当有一个更聪明的第三个我完全失踪?

+3

不要忘记枚举数是懒惰和可组合的!选项1不会导致它被枚举两次...... – 2013-03-01 17:20:22

+0

如果'MyClass'实现'IMyClass',为什么需要投射? – 2013-03-01 17:21:57

+1

你知道你总是可以在没有推理的情况下拼出类型吗?也就是,'var result = items.Select (x => new MyClass(x))。ToList();'这将返回'IEnumerable ':) – 2013-03-01 17:23:12

回答

17

我会去选择3:

var option3 = items.Select<Foo, IMyClass>(item => new MyClass(item)) 
        .ToList() 

另外,不要使用as但只投正常:

var option4 = items.Select(item => (IMyClass) new MyClass(item)) 
        .ToList() 

这两个都比使用Cast更清洁。

哦,而随着C#4 .NET 4中(由于协方差),你可以把在ToList通话,而不是一个类型参数:

var option5 = items.Select(item => new MyClass(item)) 
        .ToList<IMyClass>() 
+0

选项3和4在美学上并不适合我,但选项5肯定会。万岁选择! – ean5533 2013-03-01 17:32:58

+0

选项4不应该是'var option4 = items.Select(item =>(** IMyClass **)new MyClass(item))'? – pescolino 2013-03-01 17:45:07

+0

@pescolino:是的确 - 固定,谢谢。 – 2013-03-01 18:08:48

3

这在我看来,选项1将需要双重枚举

这是不正确的。在这两种情况下,只有当您访问ToList()时才会列举items集合。

线

var option1 = items.Select(item => new MyClass(item)).Cast<IMyClass>().ToList() 

相当于

var option1 = items.Select(item => new MyClass(item)).Select(x => (IMyClass)x).ToList() 

两者之间的唯一区别是,第一个需要每个项目两个函数调用(除非C#内联的lambda表达式某种程度上,这是我不相信是这样),而第二个选项只需要一个。

就我个人而言,我会与第二个一起作为风格的问题。

+0

+1。我个人的理由选择2 - 我不喜欢在这样的背景下投射。它表明一个人无法决定收藏哪些物品。请注意,在很多情况下,如果您不需要特定类型的'List ',您可以在需要'IEnumerable '的地方使用'IEnumerable ' - 所以您可能实际上需要在某些情况下需要强制转换以使代码更易于阅读。 – 2013-03-01 17:30:10

1

你使用哪一个是一个偏好问题,我们真的不能为你回答。

但是你的直觉如果排序正确的话Cast为你的循环增加了第二层迭代。这是非常小的,我怀疑它会产生任何性能上的可测量的差异,但Cast方法返回一个新IEnumerable对象,基本上做到这一点:

foreach (object obj in source) yield return (TResult)obj; 

效果是调用堆栈上大多是另一个层面;因为它使用yield它只会根据需要进行迭代,就像大多数其他IEnumerable方法一样。但它将不得不返回两层迭代器状态而不是一个状态。对于您而言,是否需要为自己的应用程序进行衡量。

(还要注意的是,至少根据参考源,它确实不安全的演员阵容,其中可能抛出一个异常,如果转换是无效的。这是另一个理由,更喜欢你的选择2#)

1

您可以随时到您的选择提供明确的类型参数

var option2 = items.Select<IItem,IMyClass>(item => new MyClass(item)).ToList(); 

其中IItem是可以投射物品的类型或界面。