Critical Development

Language design, framework development, UI design, robotics and more.

‘In’ Operator for .NET Framework

Posted by Dan Vanderboom on November 26, 2008

As a C#/.NET developer, one thing that I miss about Pascal is the in operator.  For example, instead of this:

IF (Letter = ‘A’) OR (Letter = ‘E’) OR (Letter = ‘I’) OR (Letter = ‘O’) OR (Letter = ‘U’) THEN …

… you can write this:

IF (Letter in [‘A’, ‘E’, ‘I’, ‘O’, ‘U’]) THEN …

It’s hard to argue that code like this is succinct, elegant, and easy to read and write.  It’s a common enough scenario to check for membership in a set that it warrants support in a language, although from inquiries I’ve made of the Microsoft C# compiler team, the syntax presents some parsing complexity and therefore implementation difficulty.

In C#, we could always do this:

char c = 'E';
if (Array.IndexOf(new char[] { 'A', 'E', 'I', 'O' }, c) != -1) // ...

But this is a lot of work (relative to the task): calling a static member on a type, instantiating an array with the new operator, comparing the result to -1 (which is a detail of our approach rather than the essense of our intention), and obscuring the value we want tested (c, the primary actor in the conditional operation) in the middle of a busy syntax.

The IEnumerable<T>.Contains<T> extension method provides an alternative:

if ((new char[] { 'A', 'E', 'I', 'O' }).Contains(c)) // ...

Using the Contains extension method is better, as it more directly expresses our intention, but we’re still creating the array explicitly and using symbols on the keyboard that slow us down–if we had a whole bunch of these to write all at once, it would create some micro-irritation.  As with the Array.IndexOf example, we’re also reversing the flow of thought from the original Pascal example, specifying a set and then checking for membership, instead of starting with a member and testing if it belongs to a set.  The end result is the same, but I personally like Pascal’s direction and flow.

It’s pretty difficult to match the simplicity of Pascal’s in operator.  But in the spirit of stubbornness and mild obsession, I came up with this extension method to reverse the direction of Contains:

public static bool In<T>(this T obj, params T[] values)
{
    for (int i = 0; i < values.Length; i++)
    {
        if (Object.Equals(obj, values[i]))
            return true;
    }
    return false;
}

With this generic extension method defined on Object, I can now write strongly-typed IN-operator-style conditional expressions like so:

if (c.In('A', 'E', 'I', 'O', 'U')) // ...

if (column.DataType.In("nchar", "nvarchar", "ntext")) // ...

I could have implemented the method more simply, like this:

return values.Contains(obj);

… but that involves another method call, and I’d rather not introduce any additional overhead for a commonly-used, global operator if it’s this easy not to.

One obligatory cautionary note: I don’t recommend adding too many extension methods on Object because of the potential for cluttering Intellisense (which currently doesn’t have an option for filtering out extension methods), but this is one of those general-purpose operators that I’d like to always have available; thus the exception.

7 Responses to “‘In’ Operator for .NET Framework”

  1. Svetlin Ralchev said

    Very good idea, Dan. The extension methods are very powerfull feature. I’m planing to post some articels using extension methods. Also we can implement BETWEEN method (as SQL).

  2. Svetlin Ralchev said

    I suggest to use Object.Equals(obj, values[i]) instead obj.Equals(values[i]), becuase if the obj is null, will be thrown exception.

  3. Dan Vanderboom said

    Good catch, Svetlin! I updated my code and the article.

  4. […] ‘In’ Operator for .NET Framework […]

  5. Guest said

    You wrote “I could have implemented the method more simply, like this:
    return values.Contains(obj);
    … but that involves another method call, and I’d rather not introduce any additional overhead for a commonly-used, global operator if it’s this easy not to.”

    This is a foolish and premature optimization. Anyone copying your code to work on other types will use the same “optimized” code which in fact is hardly optimized at all for data structures where Contains is faster than linear search. The overhead of a method call is much less than the overhead of doing linear search instead of binary search or a hash lookup. Instead, you should trust the jitter to inline Contains if the method is called frequently enough.

    Also, fwiw, obj.Equals(values[i]) can’t throw a null pointer exception — if obj is null then obj.In would have thrown an exception.

    • Aldinei said

      obj.In will NOT throw exception if obj is null. Extension methods are different from normal methods in that sense.

      • Dan Vanderboom said

        @Aldinei – I agree. Extension methods aren’t as good (or the same) as normal methods. Sometimes they’re the best tool we have, however.

Leave a comment