我有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;
}
}
有更短的路吗? 感谢
答案 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>
中,还包含setOfA
(C
的潜在未来子类)的实例,该怎么办?鉴于这个答案:
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")))