C# In() 方法? (如 SQL)
我很难找到我认为应该是相当简单的方法。
我想我们都用过这个:
select someThing from someTable where someColumn in('item1', 'item2')
在 C# 中,我必须写这样的东西:
if (someEnum == someEnum.Enum1 || someEnum == someEnum.Enum2 ||
someEnum == someEnum.Enum3)
{
this.DoSomething();
}
这可行,但它只是冗长。
出于沮丧,我编写了一个扩展方法来完成我想做的事情。
namespace System
{
public static class SystemExtensions
{
public static bool In<T>(this T needle, params T[] haystack)
{
return haystack.Contains(needle);
}
}
}
现在,我可以编写更短的代码:
if (someEnum.In(someEnum.Enum1, someEnum.Enum2, someEnum.Enum3))
this.DoSomething();
if (someInt.In(CONSTANT1, CONSTANT2))
this.DoSomethingElse();
但是,为我在框架中找不到的东西编写自己的方法感觉很脏。
你们能提供的任何帮助都会很棒, 谢谢
编辑:感谢大家的深入分析。我想我会继续使用我的 In() 方法。
I'm having a hard time finding what, I think, should be a fairly simple method.
I think we've all used this:
select someThing from someTable where someColumn in('item1', 'item2')
In C#, I've have to write stuff like this:
if (someEnum == someEnum.Enum1 || someEnum == someEnum.Enum2 ||
someEnum == someEnum.Enum3)
{
this.DoSomething();
}
This works, but it's just wordy.
Out of frustration, I wrote an extension method to accomplish what I'm trying to do.
namespace System
{
public static class SystemExtensions
{
public static bool In<T>(this T needle, params T[] haystack)
{
return haystack.Contains(needle);
}
}
}
Now, I can write shorter code:
if (someEnum.In(someEnum.Enum1, someEnum.Enum2, someEnum.Enum3))
this.DoSomething();
if (someInt.In(CONSTANT1, CONSTANT2))
this.DoSomethingElse();
It feels dirty, however, to write my own method for something that I just can't find in the framework.
Any help you folks can offer would be great,
Thanks
EDIT: Thanks everyone for the in-depth anaylsis. I think I'll keep using my In() method.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(8)
没有像您所拥有的现有扩展方法。让我解释一下为什么我这么认为(除了明显的“因为它没有被指定、实现、测试、记录等”原因)。
基本上,这种实现必然是低效的。从传递给
In
的参数构造一个数组(如使用params
关键字时发生的情况)是一个 O(N) 操作,会导致无谓的 GC 压力(来自构造一个新的T[]
对象)。Contains
然后枚举该数组,这意味着您的原始代码的执行时间增加了一倍多(而不是通过短路求值进行部分枚举,您将获得一个完整枚举,然后是部分枚举)枚举)。通过使用采用
T 类型的 1 到 X 个参数的 X 重载替换扩展方法的
其中 X 是一些合理的数字...比如 1-2 打。但这并没有改变这样的事实:您将 X 值传递到调用堆栈的新级别,只是为了检查可能少于 X 的值(即,它不会消除性能损失,只会减少性能损失)。params
版本,可以在一定程度上减轻由数组构造引起的 GC 压力然后还有另一个问题:如果您打算使用此
In
扩展方法来替代一堆链式||
比较,那么您可能会忽略其他一些内容。使用||
,您可以得到短路评估;对于传递给方法的参数来说,情况并非如此。对于枚举来说,就像您的示例一样,这并不重要。但请考虑以下代码:上面的(奇怪/糟糕的 - 仅用于说明)代码不应抛出
IndexOutOfRangeException
(它可能会抛出NullReferenceException
,但这与要点无关)我正在做)。然而,使用In
的“等效”代码很好地可以:我并不是说您的
In
扩展想法是一个坏想法。在大多数情况下,如果使用得当,它可以节省打字时间,并且性能/内存成本不会引人注目。我只是提供我的想法,为什么这种方法不适合作为内置库方法:因为它的成本和限制可能会被误解,导致过度使用和次优代码。There's no existing extension method like what you have. Let me explain why I think that is (aside from the obvious "because it wasn't specified, implemented, tested, documented, etc." reason).
Basically, this implementation is necessarily inefficient. Constructing an array from the parameters passed to
In
(as happens when you use theparams
keyword) is an O(N) operation and causes gratuitous GC pressure (from the construction of a newT[]
object).Contains
then enumerates over that array, which means your original code has been more than doubled in execution time (instead of one partial enumeration via short-circuited evaluation, you've got one full enumeration followed by a partial enumeration).The GC pressure caused by the array construction could be alleviated somewhat by replacing the
params
version of the extension method with X overloads taking from 1 to X parameters of typeT
where X is some reasonable number... like 1-2 dozen. But this does not change the fact that you're passing X values onto a new level of the call stack only to check potentially less than X of them (i.e., it does not eliminate the performance penalty, only reduces it).And then there's another issue: if you intend for this
In
extension method to serve as a replacement for a bunch of chained||
comparisons, there's something else you might be overlooking. With||
, you get short-circuited evaluation; the same doesn't hold for parameters passed to methods. In the case of an enum, like in your example, this doesn't matter. But consider this code:The above (weird/bad -- for illustration only) code should not throw an
IndexOutOfRangeException
(it could throw aNullReferenceException
, but that's irrelevant to the point I'm making). However, the "equivalent" code usingIn
very well could:I'm not saying your
In
extension idea is a bad one. In most cases, where used properly, it can save on typing and the performance/memory cost will not be noticeable. I'm just offering my thoughts on why a method of this sort would not be appropriate as a built-in library method: because its costs and limitations would likely be misunderstood, leading to over-use and suboptimal code.我认为您已经接近使用
Contains
调用。这是 Linq 查询的常见方法。
顺便说一句:我喜欢你的扩展方法,我认为这在某些情况下非常有用。
I think you are close with using the
Contains
call.This is the common approach for Linq queries.
BTW: I like your extension method, I think that could be extremely useful in certain situations.
差不多就这样了。您的
In()
扩展方法非常好。即使您使用的是按照 SQL 建模的 LINQ,您仍然必须使用Contains
来指示在 SQL 中使用IN
。翻译为:
That's pretty much it. Your
In()
extension method is pretty nice. Even if you are using LINQ, which is modeled after SQL, you still have to useContains
to indicate usingIN
in the SQL.Translates to:
我不知道别的事。
对我来说,我认为像您一样编写这样的扩展方法就可以了,对于您经常需要的操作并且您想要一个可读且方便的语法。这就是扩展方法的好处。
周围只有数百种有用的扩展方法。您可能会问其中很多,为什么它们没有包含在 .NET 框架中?
并非所有内容都可以包含在语言中。所以自己写一个库,希望以后能被收录。
I don't know anything else.
For me, I think it is just ok to write such extension methods as you did, for operations you often need and you want a readable and handy syntax. That's what extension methods are good for.
There are just hundreds of useful extension methods around. You could ask for many of them, why aren't they included in the .NET framework?
Not everything can be already included in a language. So write your own library and hope that it will be included in the future.
如果您愿意,您可能会对 FlagAttibute 感兴趣尤其是使用枚举来做到这一点。
You might be interested in the FlagAttibute if you're wanting to do this particularly with Enums.
语言不可能让所有人都满意,但无论是你做的,还是编译器做的,都没有太大区别。该语言为您提供 Any 和包含
在您的世界中可能很好,但当其他人有拿起你的代码会让他们感到困惑。
Languages can't please everyone, but whether you do it, or the compiler does it there isn't much difference. The language gives you Any & Contains
In might be nice in your world, but when someone else has to pick up your code it will be confusing to them.
您可以通过使用表达式做一些更好的事情,这将允许在像 Linq2Sql 这样的情况下正确利用该构造。
You could do something a little better, by using Expressions, this will allow the construct to be properly utilized in cases like Linq2Sql.
如果您希望返回不同的值,可以使用 .Intersect 扩展方法。例如。
You could use the .Intersect extension method if you would like distinct values returned. Eg.