IEnumerable <t> .Single and casting </t>

时间:2011-11-03 08:16:01

标签: c# c#-4.0

我有2个对象A和B. B继承自A并具有更多属性。 我有IEnumerable {A}只包含B对象。 我想做的是:

list.Single(b => b.PropertyThatOnlyExistOnB == "something")

我希望这样的事情可以发挥作用:

list.Single((B) b => b.PropertyThatOnlyExistOnB == "something")

但它没有编译。现在我只是这样做:

B result = null;
foreach (b in list)
{
     if((B)b.PropertyThatOnlyExistOnB == "something")
     {
      result = (B)b;
     }
}

有更短的路吗? 感谢

4 个答案:

答案 0 :(得分:9)

使用Enumerable.OfType<TResult>扩展程序进行过滤/转换。

list.OfType<B>().Single(b => b.PropertyThatOnlyExistOnB == "something")

答案 1 :(得分:4)

虽然我最喜欢@ VirtualBlackFox的答案,但为了完整起见:以下是如何让你的想法发挥作用:

list.Single(b => ((B)b).PropertyThatOnlyExistOnB == "something");

除了你的一些语法混淆之外,你并没有那么遥远。 b => EXPRESSION语法表示lambda表达式。你不能在=>之前开始改变这些东西,除非你想添加(或删除)参数:

* `x => LAMBDA_WITH_ONE_PARAMETER`
* `(x) => LAMBDA_WITH_ONE_PARAMETER`
* `() => LAMBDA_WITH_NO_PARAMETERS`
* `(x, y, z) => LAMBDA_WITH_THREE_PARAMETERS`

答案 2 :(得分:2)

  

我的IEnumerable<A>只包含B个对象。

我会质疑这个关于你的变量的陈述。您已指定它是IEnumerable<A>,但它仅包含B的实例。这样做的目的是什么?如果您在所有情况下明确只需要B的实例,那么最好是IEnumerable<B>,因为它可以保护可能在编译时捕获的问题。

考虑以下情况,我想你可能会有类似的代码:

var setOfA = // Get a set of A.
DoSomethingWithA(setOfA);

var instanceOfB = GetInstanceOfB(setOfA);

在这种情况下,我可以理解IEnumerable<A>完全有效,除非您想执行后一个操作GetInstanceOfB。让我们想象一下,定义是:

B GetInstanceOfB(IEnumerable<A> setOfA)
{
    return // The answer to your question.
}

现在,我希望您看到的最初问题是,您将所有卡片放在您的列表(我的示例中为setOfA)的概念上,始终只包含{的实例{1}}。虽然您可以保证从开发人员的角度来看,编译器不能做出这样的假设,但它只能保证B(列表)是setOfA,并且其中存在潜在的问题。

查看所提供的答案(根据您的想法,所有答案都完全有效[@VirtualBlackFox是最安全的答案]:

  

我的IEnumerable<A>只包含B个对象。

如果在将来的某个更改IEnumerable<A>中,还包含setOfAC的潜在未来子类)的实例,该怎么办?鉴于这个答案:

A

如果list.Single(b => ((B)b).PropertyThatOnlyExistOnB == "something"); 实际上是:setOfA,该怎么办?您可以看到显式转换[C B B]将导致(B)b被抛出。由于InvalidCastException操作的性质,它将继续枚举,直到第一个实例出现谓词(Single)失败,或者抛出异常。在这种情况下,可能会抛出异常,这可能是未处理的。这个答案类似于:

PropertyThatOnlyExistOnB == "something"

鉴于这个答案:

list.Cast<B>().Single(b => b.PropertyThatOnlyExistOnB == "something");

在相同的情况下,异常将作为list.Single<A>(b => (b as B).PropertyThatOnlyExistOnB == "something") 的抛出实例出现,因为NullReferenceException的实例无法安全地类型转换为C

现在,不要误会我的意思,我不会选择那些答案,因为我说他们完全有效,因为你提出了问题。但是在您的代码发生变化的情况下,那些完全有效的答案将成为未来潜在的问题。

鉴于这个答案:

B

这允许您安全地将强制类型转换为list.OfType<B>.Single(b => b.PropertyThatOnlyExistOnB == "something"); 的潜在子集,实际上A,并且编译器可以保证您的谓词仅用于B

但这会让我发现代码中的关键点是尝试处理您的IEnumerable<B>但执行您真正需要IEnumerable<A>的操作。在这种情况下,您不应该重构此代码以使其具有明确的方法:

IEnumerable<B>

该方法设计的更改确保它只会明确接受一组有效的B GetMatchingInstanceOfB(IEnumerable<B> setOfB) { if (setOfB == null) throw new ArgumentNullException("setOfB"); return setOfB.Single(b => b.PropertyThatOnlyExistOnB == "something"); } ,并且您不必担心该方法中的转换。该方法仅负责匹配B的单个项目。

这当然意味着你需要将你的演员推向不同的水平,但这仍然更明确:

B

我还假设您在谓词失败的情况下有足够的错误处理,其中所有实例都是var b = GetMatchingInstanceOfB(setOfA.OfType<B>()); ,例如,多于1个项目满足B

这可能是一个关于审查代码的无意义的咆哮,但我认为值得考虑可能出现的意外情况,以及如何调整变量可以为您节省潜在的麻烦。

答案 3 :(得分:1)

这应该可以正常工作:

list.Single<A>(b => (b as B).PropertyThatOnlyExistOnB == "something")

如果您不想冒被抛出异常的风险,您可以这样做:

list.Single<A>(b => ((b is B)&&((b as B).PropertyThatOnlyExistOnB == "something")))