2011-11-03 59 views
1

我有2个对象A和B. B是继承自A并且有一些更多的属性。 我有只包含B对象的IEnumerable {A}。 我想要做的是:IEnumerable <T>。单独和铸造

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; 
    } 
} 

有没有更简单的方法? 谢谢

回答

9

使用Enumerable.OfType<TResult>扩展方法来筛选/转换。

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

+1打我到它30秒:) – MatthewKing

+0

不错,干净,非常感谢! –

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` 
+0

您有风险引发InvalidCastExceptions。你如何确保只有B的实例在枚举中? –

+0

我不是。 OP表示他肯定,只有'B'的实例正在列举中。有时你确定;)另外,如果枚举中没有'B'实例,'Single()'也会引发一个异常。只是说。 –

+0

@Mthethe Abbot:另外,我只是想告诉OP如何让他的代码正确。我记得说我更喜欢(和btw,+1)VirtualBlackFox的答案! –

1

这应该很好地工作:

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

如果你不想冒险例外被抛出,你可以这样做:

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

您不会冒险可能NullReferenceException其中该项目不是B的实例? –

+0

它的NullReferenceException或InvallidCastException(当你((B)B).Property。你的选择;) – neurotix

+0

请参阅我的维基答案下面;-) –

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在我的例子),总是只打算包含B的实例。虽然您可以保证从您的开发人员的角度来看,编译器可以不做这样的假设,但只能保证setOfA(列表)是IEnumerable<A>,并且其中存在潜在的问题。

综观提供的答案(所有这些都是非常有效的[@VirtualBlackFox是最安全的答案]给你的想法):

IEnumerable<A>只含有B的对象。

,如果在将来的某个变化,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>()); 

我也假设你有足够的错误处理到位的情况下,在所有实例为B的情况下谓词将失败,例如,多于一个项目满足PropertyThatOnlyExistOnB == "something"

这可能是一个无意义的评论你的代码的咆哮,但我认为这是值得考虑的可能出现的意外情况,以及如何调整变量可以为你节省潜在的头痛在未来。