Click here to Skip to main content
Click here to Skip to main content

Having fun with C# 3.0 extensions

By , 7 Dec 2008
Rate this:
Please Sign up or sign in to vote.

Introduction

A couple of weeks ago, I finally managed to get a copy of Visual Studio 2008. Toying around with the new features of C# 3.0, I discovered some interesting tricks using extensions. Extensions are static functions introduced in C# 3.0. They allow developers to extend existing types without having to change the original code.

Example

Creating a new extension is relatively simple. Define a static function in a static class and pass the type you want to extend as a parameter. It must be the first parameter and has to be marked with the keyword this. Additional parameters are also possible.

namespace App.Utility
{
    public static class Extensions
    {
       public static string AddExclamationMark(this string text)
       {
          return text + "!";
       }
 
       public static string AddAditionalText(this string text, string add)
       {
          return text + add;
       }
    }
}

The class name you choose is irrelevant since the functions can now be called by the defined type. Visual Studio 2008 also supports them in IntelliSense. The best way to use extensions is to collect them all in a static utility class. If you plan on using extensions a lot, you might want to split them in different classes and namespaces.

//calling extension functions
"Hello World".AddExclamationMark();

Unluckily, extensions cannot be defined as properties. You also can't override existing methods.

String validation C# 1.0 to 3.0

Validating strings is a common task, especially checking if they aren't null or empty. Extending strings with custom functions can make life a lot easier. Let's look first at the possible implementations in C# 1.0 and 2.0:

string input = "Lorem ipsum"; 

//in C# 1.0 it would look like this:
if(input != null && input != "")
{
   //do something
}

//we could also use the static property Empty
//and Trim to exclude strings only consisting of spaces 
if(input != null && input.Trim() != string.Empty)
{
   //do something
}

//C# 2.0 introduced new static functions to the string class
if(!string.IsNullOrEmpty(input))
{
   //do something
}

I often use IsNullOrEmpty, but I don't actually like it much. First of all, I usually want to know if the string has a value, not the other way round. Secondly, this function also accepts strings consisting only of empty spaces, and I can't use Trim since the string might be null. To solve this in C# 2.0, I would have to create a static utility method:

namespace App.Utility
{
    public static class StringHelper
    {
       public static bool HasValue(string s)
       {
          return s != null && s.Trim() != string.Empty;
       }
    }
}

if(Utility.StringHelper.HasValue(input))
{
   //do something
}

The resulting code is cumbersome, ugly, and easily forgotten. Included in a common framework, chances are high that other developers in your team won't find it, let alone use it.

Enter Extension Functions

The only thing we have to do now is add the keyword this before the first parameter:

public static bool HasValue(this string s)
{
  return s != null && s.Trim() != string.Empty;
}

HasValue is then available on all strings. As it is now listed in the IntelliSense box, it is also likely that the rest of your team will actually use the function.

using App.Utility;
//...
 
if(input.HasValue())
{
   //do something
}

Now to the interesting part. As we all know, calling functions on an uninitialized object results in an exception. This isn't automatically true for extensions.

bool b;
b = "Lorem ipsum".HasValue();
b = "".HasValue();
 
string s = null;
b = s.HasValue(); //yes, this doesn't throw an exeption

How come this works? Well, extensions only look like normal function calls. They are actually still static functions of the static class (in this case, StringHelper). So, the code is identical to this:

if(StingHelper.HasValue(input)) 
{
   //do something
}

Jumping the sharp

I found calling functions on non-initialized objects quite intriguing. Couldn't this always be used instead of verifying that an object isn't null? The following examples shouldn't be used in production code (well, except if you really want to).

Let's assume we have a database filled with personal information. Each entry is represented by the class Person. Every person should have a birth year, but our data is rather faulty.

using System;
using System.Collection.Generics;
 
public class Person
{
    private string name;
    private int birthYear;

    public string Name
    {
       get { return name; }
       set { name = value; }
    }
    
    public int BirthYear
    {
       get { return birthYear; }
       set { birthYear = value; }
    }
}
 
public class Programm
{
    public static void Main()
    {
       List<person> personList = new List<person>();
       personList.Add(new Person(){ Name = "John", BirthYear = 1980 });
       personList.Add(new Person(){ Name = "Jane", BirthYear = 1987 });
       personList.Add(new Person());
       personList.Add(null);
       personList.Add(new Person(){ Name = "Bob", BirthYear = 0 });
 
       //print each birth year above 0
       foreach(Person person in personList)
       {
          if(person != null)
          {
             if(person.BirthYear > 0)
             {
                Console.WriteLine(person.BirthYear);
             }
          }
       }
    }
}

We have to check that each Person isn't null before we access the BirthYear property. With Extensions, we don't have to. Instead, we return a default value.

public static class PersonExtension
{
    public static int GetBirthYear(this Person person)
    {
       if(person != null)
       {
          return person.BirthYear;
       }
       return 0;
    }
}

//...
 
//print each birth year above 0
foreach(Person person in personList)
{
  if(person.GetBirthYear() > 0)
  {
     Console.WriteLine(person.BirthYear);
  }
}

I don't think it is a good idea to start writing extensions for every single property though!

History

  • 12-07-2008
    • Original article.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Greg Orchard
Web Developer
Germany Germany
No Biography provided

Comments and Discussions

 
GeneralMy vote of 5 PinmemberOmar Gamil2-May-11 2:03 
GeneralSomething new... [modified] Pinmembertonyt10-Dec-08 5:12 
GeneralNothing new PinmemberPIEBALDconsult8-Dec-08 2:07 
GeneralMy vote of 1 PinmemberRamon Smits7-Dec-08 23:23 
GeneralDon't compare to String.Empty [modified] PinmemberJiaozi7-Dec-08 22:44 
GeneralRe: Don't compare to String.Empty PinmemberPIEBALDconsult8-Dec-08 2:08 
GeneralHome work not done... PinmemberM Aamir Maniar7-Dec-08 21:03 
GeneralAn alternative to the 'null Person' problem .... PinmemberHightechRider7-Dec-08 13:47 
GeneralUsing Extensions on null references PinmvpColin Angus Mackay7-Dec-08 12:26 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web01 | 2.8.140415.2 | Last Updated 7 Dec 2008
Article Copyright 2008 by Greg Orchard
Everything else Copyright © CodeProject, 1999-2014
Terms of Use
Layout: fixed | fluid