Showing posts with label ExtensionMethods. Show all posts
Showing posts with label ExtensionMethods. Show all posts

SQL Like 'In' Function For .NET

SQL has a wonderful terse and natural syntax for checking if an item is inside of a collection by using the IN keyword. Which can be expressed like this:

expression IN (value_1, value_2, .... value_n)

In .NET, we can preform the same operation with .Contains() method on any enumerable collection which:

Determines whether a sequence contains a specified element

Beginning with VB 2010, we can even do this on the fly by using the Collection Initializers.

For example, to check whether a variable called personName was either "Sally" or "Jenny", we could use the following expression:

{"Sally","Jenny"}.Contains(personName)

However, I think this syntax leaves something to be desired. The verbiage is all wrong. I don’t really care if a collection contains some item. I care if an item is in a collection. Of course, logically, this is performing the same operation, but I want the personName variable to be in the drivers seat. I want personName to be the subject that is verbing against the other items.

For a bit of syntactic sugar, we can add a generic extension method to take in an ParamArray and check if the extended element falls inside that array.

Here’s the In method:

Note: In needs to be inside of square brackets because it is a Protected Keyword.

Visual Basic

''' <summary>
''' Determines if the Item is contained within the listed array
''' </summary>
''' <typeparam name="T">The type of object</typeparam>
''' <param name="item">The calling item</param>
''' <param name="range">An array or comma separated list of the items to check against the calling</param>
''' <returns>True if item is found in list</returns>
''' <remarks>Provides syntatic sugar by reordering the subject of the IEnumerable.Contains method</remarks>
<Extension()>
Public Function [In](Of T)(ByVal item As T, ByVal ParamArray range() As T) As Boolean
    Return range.Cast(Of T).Contains(item)
End Function

C Sharp

public static class Extensions
{
    /// <summary>
    /// Determines if the Item is contained within the listed array
    /// </summary>
    /// <typeparam name="T">The type of object</typeparam>
    /// <param name="item">The calling item</param>
    /// <param name="range">An array or comma separated list of the items to check against the calling</param>
    /// <returns>True if item is found in list</returns>
    /// <remarks>Provides syntatic sugar by reordering the subject of the IEnumerable.Contains method</remarks>
    public static bool In<T>(this T item, params T[] range)
    {
        return range.Contains(item);
    }
}

Throw this inside any module in your assembly, preferably one named something like Utilities or ExtensionMethods. Now we can call like this:

personName.In("Sally","Jenny")

If you’re checking against a predefined list you can pass that in as a parameter and cast back into an array.

Personally, I take this utility method with me wherever I go. I find it incredibly helpful for checking if an item exists within a defined range. Once you start using it, you won’t stop, and I think that’s a good thing! For my money, it substantially improves readability, especially if you find yourself working on older code bases without collection initializers.

Extending XmlReader Class: IsEndElement()

Introduction

You may find, as I did, the lack of symmetry in the XmlReader Class quite odd. If we look at the NodeType property, which exposes the XmlNodeType enumeration, we find an EndElement, but no StartElement (The Element member actually only identifies opening xml tag elements, but it’s not this shortage on which I’d like to elaborate). Only when we look at the methods available to the class do we see an IsStartElement() method for evaluation, but without a corollary method named something like: IsEndElement().

There are dozens of ways of getting around this deficiency, but I’m all for visually appealing code that leaves other coders with a easy and quick understanding of what you are attempting to do. Checking if the reader is on an opening tag with IsStartElement and then finding closing tags by evaluation the reader’s NodeType property might work correctly, but just looks wrong to me.

Extension Methods

What I’d like to do is create a method that looks and feels like the IsStartElement() function, but instead evaluates if the reader is currently on an EndElement node type. This is where extension methods come into play. Extension methods allow you to extend the functionality of a built in class with custom methods that act as if they were native to the class. I think they are best suited for when you are of the sincere belief that method deficit is a slight oversight of the framework designers, and if given the opportunity, they would happily accept your additional coding to improve the underlying code. Since all instances of this type or any derived type will have automatic access to any extension methods written on top of a particular class, you want to be careful with their implementation .

The first step is to open your project, right click on it, click add, then select module:

Add Module

Then give your module a name. I like to create a single Module for all extensions with a name like ExtensionMethods.

IsEndElement Code

First, you’ll need an imports(vb) or using(c#) statement with the extensions Namespace:

Imports System.Runtime.CompilerServices

Then, add the following code to your module:

<Extension()>  
Public Function IsEndElement(ByVal xmlReader As XmlReader, 
                             Optional ByVal name As String = "") As Boolean  
    If xmlReader.NodeType = XmlNodeType.EndElement Then  
        If name = "" Then  
            'node is end element, name value not set  
            Return True  
        Else  
            If xmlReader.Name = name Then  
                'node is end element, AND named the same as parameter  
                Return True  
            Else  
                'node is end element, but name is diff than parameter  
                Return False  
            End If  
        End If  
    Else  
        'node is not an end element  
        Return False  
    End If  
End Function  

And voilà! Now you can call your code on native objects with full IntelliSense:

IntelliSense