开发者

C# In() method? (like Sql)

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开发者_如何学C 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.


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 the params keyword) is an O(N) operation and causes gratuitous GC pressure (from the construction of a new T[] 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 type T 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:

if (0 == array.Length || 0 == array[0].Length || 0 == array[0][0].Length)
{
    // One of the arrays is empty.
}

The above (weird/bad -- for illustration only) code should not throw an IndexOutOfRangeException (it could throw a NullReferenceException, but that's irrelevant to the point I'm making). However, the "equivalent" code using In very well could:

if (0.In(array.Length, array[0].Length, array[0][0].Length)
{
    // This code will only be reached if array[0][0].Length == 0;
    // otherwise an exception will be thrown.
}

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.


I think you are close with using the Contains call.

List<strong> items = List<string>{ "item1", "item2", "item3" };
bool containsItem = items.Contains( "item2" );

This is the common approach for Linq queries.

from item in ...
where items.contains( item )
select item

BTW: I like your extension method, I think that could be extremely useful in certain situations.


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 use Contains to indicate using IN in the SQL.

from a in table
where SomeArray.Contains(a.id)
select a;

Translates to:

select * from table a where a.id in (.....)


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.


You might be interested in the FlagAttibute if you're wanting to do this particularly with Enums.


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.


You could do something a little better, by using Expressions, this will allow the construct to be properly utilized in cases like Linq2Sql.


You could use the .Intersect extension method if you would like distinct values returned. Eg.

List<string> test = new List<string>() { "1", "2", "2", "3" };
List<string> test2 = new List<string>() { "1", "2" };

var results = test.Intersect<string>(test2);
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜