10

在我的项目中,我有一个MyClasswhich implements IMyClassIMyClass我需要通过转换其他项目的列表来返回一个列表。为简单起见,假设我可以MyClass通过将另一个项目传递给它的构造函数来创建一个,即new MyClass(item).

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

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 的其他原因?或者,当我完全错过了一个更聪明的第三个想法时,我是否在比较两个愚蠢的想法?

4

4 回答 4

18

我会选择选项3:

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

或者,不要使用as而只是正常投射:

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

这两个看起来都比使用Cast.

哦,从带有 .NET 4 的 C# 4 开始(由于协变),您可以在ToList调用上放置一个类型参数:

var option5 = items.Select(item => new MyClass(item))
                   .ToList<IMyClass>()
于 2013-03-01T17:23:34.993 回答
3

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

这不是真的。在这两种情况下,items仅当您到达时才会枚举集合ToList()

线

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,我不相信是这种情况),而第二个选项只需要一个。

就个人而言,作为风格问题,我会选择第二个。

于 2013-03-01T17:22:18.573 回答
1

您使用哪一个是一个偏好问题,我们真的无法为您回答。

但是如果你的直觉是正确的,Cast就会为你的循环添加第二层迭代。它非常小,我怀疑它会在性能上产生任何可测量的差异,但是该Cast方法返回一个IEnumerable基本上可以执行此操作的新对象:

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

效果主要是调用堆栈上的另一个级别;因为它使用yield它只会按需迭代,就像大多数其他IEnumerable方法一样。但它必须通过两级迭代器状态而不是一级返回。这对您是否重要是您需要为自己的应用程序衡量的东西。

(另请注意,至少根据参考源,它会进行不安全的强制转换,如果强制转换无效,可能会引发异常。这是更喜欢您的选项#2 的另一个原因。)

于 2013-03-01T17:23:07.393 回答
1

您始终可以为您的 Select 提供显式类型参数

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

其中 IItem 是可以将项目转换为的类型或接口。

于 2013-03-01T17:28:39.297 回答