IEnumerable.单一和铸造
我有 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;
}
}
有没有更短的方法? 谢谢
I a have 2 objects A and B. B is inherited from A and has some more properties.
I have IEnumerable{A} that contains only B objects.
What I want to do is:
list.Single(b => b.PropertyThatOnlyExistOnB == "something")
I would have expect something like this to work:
list.Single((B) b => b.PropertyThatOnlyExistOnB == "something")
But it doesn't compile. For now I just doing:
B result = null;
foreach (b in list)
{
if((B)b.PropertyThatOnlyExistOnB == "something")
{
result = (B)b;
}
}
Is there a shorter way?
Thanks
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
使用
Enumerable.OfType
过滤/转换的扩展方法。Use the
Enumerable.OfType<TResult>
extension methods to filter/cast.虽然我最喜欢@VirtualBlackFox的答案,但为了完整起见:以下是如何让你的想法发挥作用:
你并没有偏离轨道,只是你的一些语法感到困惑。
b =>; EXPRESSION
语法表示 lambda 表达式。除非您想添加(或删除)参数,否则您无法开始更改=>
之前的内容:Although I like @VirtualBlackFox's answer best, for completeness sake: Here is how to get your idea to work:
You weren't that far off track, except that you got some of the syntax confused. The
b => EXPRESSION
syntax denotes a lambda expression. You can't start altering the stuff before the=>
, unless you want to add (or remove) arguments:我会对这个关于你的变量的陈述提出质疑。您已指定它是
IEnumerable
,但它仅包含B
的实例。这样做的目的是什么?如果您在所有情况下都明确只需要B
实例,那么最好将其设置为IEnumerable
,因为它可以防止可能在以下位置捕获的问题:编译时间。考虑以下内容,我想您可能有一些类似于以下内容的代码:
在这种情况下,我可以理解
IEnumerable
是完全有效的,除非您想执行后一个操作,GetInstanceOfB
。让我们想象一下,定义是:现在,我希望您看到的最初问题是,您将所有卡片都放在这样的概念上:您的列表(在我的示例中为
setOfA
)始终只会去包含B
的实例。虽然您可以保证从开发人员的角度来看,编译器不会做出这样的假设,但它只能保证setOfA
(列表)是IEnumerable
,这就是潜在的问题。查看提供的答案(根据您的想法,所有这些答案都是完全有效的[@VirtualBlackFox 是最安全的答案]):
如果在将来的某些更改中,
setOfA
还包含C
的实例(A
的潜在未来子类),该怎么办?给出这个答案:如果
setOfA
实际上是:[CBB]
会怎样。您可以看到显式转换(B)b
将导致抛出InvalidCastException
。由于Single
操作的性质,它将继续枚举,直到第一个实例失败谓词(PropertyThatOnlyExistOnB == "something"
),或者出现异常抛出。在这种情况下,可能会抛出意外的异常,并且可能未处理。这个答案类似于:给出这个答案:
在相同的情况下,异常将作为
NullReferenceException
的抛出实例出现,因为C
的实例无法安全地键入转换为B
。现在,请不要误会我的意思,我不会在这些答案中找漏洞,正如我所说,考虑到您的问题的范围,它们是完全有效的。但在代码发生更改的情况下,那些完全有效的答案将成为未来潜在的问题。
给出这个答案:
这允许您安全地类型转换为
A
的潜在子集,实际上是B
,并且编译器可以保证您的谓词仅用于一个IEnumerable
。但这会让我发现代码中的接合点正在尝试处理您的
IEnumerable
但在您真正想要IEnumerable
的地方执行操作。在这种情况下,您不应该重构此代码以使其可能具有显式方法吗:方法设计的更改确保它仅显式接受一组有效的
B
,而您不需要不必担心该方法中的演员表。该方法只负责匹配B
的单个项目。这当然意味着您需要将转换推向不同的级别,但这仍然更加明确:
我还假设您在所有实例都失败的情况下有足够的错误处理。 >B,例如,超过 1 个项目满足
PropertyThatOnlyExistOnB == "something"
。这可能是关于审查代码的毫无意义的咆哮,但我认为值得考虑可能出现的意外情况,以及调整变量如何可能在未来为您省去潜在的麻烦。
I would question this statement about your variable. You've specified that it is an
IEnumerable<A>
, but it contains only instances ofB
. What is the purpose of this? If you are explicitly only requiring instances ofB
in all circumstances, it would be better for this to be anIEnumerable<B>
, as it safeguards problems that could be caught at compile time.Consider the following, I would imagine that you may have some code similar to:
In this case, I can understand that an
IEnumerable<A>
is perfectly valid, except when you want to perform the latter operation,GetInstanceOfB
. Let's imagine, the definition is:Now, the initial problem I hope you see, is that you're putting all your cards on the notion that your list (
setOfA
in my example), is always only going to contain instances ofB
. While you may guarantee that from your developer point of view, the compiler can make no such assumption, it can only guarantee thatsetOfA
(list) is anIEnumerable<A>
, and therein lies the potential issue.Looking at the answers provided (all of which are perfectly valid [@VirtualBlackFox being the safest answer] given your notion):
What if, in some future change,
setOfA
, also contains an instance ofC
(a potential future subclass ofA
). Given this answer:What if
setOfA
is actually:[C B B]
. You can see that the explicit cast(B)b
will cause anInvalidCastException
to be thrown. Because of the nature of theSingle
operation, it will continue to enumerate until the first instance that something fails the predicate (PropertyThatOnlyExistOnB == "something"
), or an exception is thrown. In this instance, the exception could be thrown which is unexpected, and likely unhandled. This answer, is similar to:Given this answer:
In the same situation, the exception would arise as a thrown instance of
NullReferenceException
, because the instance ofC
cannot be safely type cast toB
.Now, don't get me wrong, I am not picking holes with those answers, as I said they are perfectly valid given the remit of your question. But in circumstances where your code changes, those perfectly valid answers become potential future issues.
Given this answer:
This allows you to safely type cast to a potential subset of
A
that are in factB
, and the compiler can guarantee that your predicate is only being used on anIEnumerable<B>
.But this would lead me to discovering that the juncture in your code is trying to handle your
IEnumerable<A>
but perform an operation where you really want yourIEnumerable<B>
. In which case, shouldn't you refactor this code to possibly have an explicit method:The change in the design of the method ensures that it will only explicitly accept a valid set of
B
, and you don't have to worry about your cast within that method. The method is responsible only for matching a single item ofB
.This of course means you need to push your cast out to a different level, but that still is much more explicit:
I'm also assuming that you have sufficient error handling in place in circumstances where the predicate will fail where all instances are
B
, e.g., more than 1 item satisfiesPropertyThatOnlyExistOnB == "something"
.This might have been a pointless rant about reviewing your code, but I think it is worth considering unexpected situations that could arise, and how potentially tweaking your variables can save you a potential headache in the future.
这应该可以正常工作:
如果您不想冒抛出异常的风险,您可以这样做:
This should work fine:
If you dont want to risk exceptions to be thrown you can do this: