A quick tour of C# 3.0’s new language features

With Visual Studio 2008 now fully released, I take a quick look at some of the new language features in C# 3.0.


I’ll start the overview with the introduction of two classes, Person, and MyExtensions.

    public partial class Person
    {
        public Person()
        {
            LogCreation();
        }

        // automatic properties:
        public int Age { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public short NumberOfChildren { get; set; }

        // partial method:
        partial void LogCreation();
    }

    // this could (and probably would) be in a different file:
    public partial class Person
    {
        // partial method implementation:
        partial void LogCreation()
        {
            Console.WriteLine("Person created");
        }
    }

The Person class introduces two new language features.

Partial Methods

The LogCreation method in the Person class has been decorated with the ‘partial’ keyword, meaning that it is a ‘partial method’.

Partial methods add a lightweight event handling mechanism to C#. They’re basically methods where the declaration or prototype is specified in the declaration of a partial class, but the implementation can only be provided in another declaration for that partial class. In other words, a partial method will only compile if it is specified in one partial class definition and is the implementation is provided in another partial class definition, as shown above.

The ‘clever’ thing is that because no definition for the method is provided, no IL code is produced for that method, so the resulting assembly is no bigger or slower than if the partial method wasn’t there in the first place. This then begs the question – if an implementation isn’t provided, why have the prototype at all? Well, you could set up some ‘hooks’ into your code for specific methods, that may or may not be implemented by a developer.

The Person class invokes the partial method LogCreation, and because an implementation is provided, the code is executed. If the second partial class definition didn’t exist, or the method wasn’t implemented, the code would not be executed. In practice, they are used by some new LINQ (more on that shortly) SQL entity classes as a hook for property value change notification. A partial method is called before a property value changes, and a partial method is called after the property value has changed. If you wish to hook into these, you can implement the methods in your partial class and be notified. If not, the partial methods are ‘removed’ from the assembly’s output.

Syntactically, there are a few rules for partial methods:

  1. Can only be defined and implemented in partial classes
  2. Must specify the partial modifer
  3. Must not specify an access modifier, but are private. If an access modifier (even if it’s private) is specified, you’ll get a compiler error
  4. They must return void

A little less strict in some other areas:

  1. They can have arguments
  2. The don’t have to be implemented
  3. They can be static

The second class, MyExtensions, is shown below:

    public static class MyExtensions
    {
        // extension method
        public static bool IsEven(this int number)
        {
            return number % 2 == 0;
        }
    }

The MyExtensions class shown above may look familiar at first glance, but the this keyword in the method arguments makes it stand out as an extension method.

Extension Methods

Basically, extension methods are a way of ‘extending’ a .NET type with a method so that it may be called on an instance of that method.

The example shown illustrates the rules that an extension method must be defined as static, in a static class, and contain the this keyword in the method arguments list. The type of argument is the .NET type that is being extended.

The nice thing is that when you have an extension method, you can do something like this:

Extension methods in the Visual Studio 2008 IDE

Our example extends int, and the Visual Studio 2008 IDE indicates our extension method with a little icon and associated text. Perhaps I need to get out a little more, but I find extension methods quite exciting.

Collection and Object initialization

// collection initializer:
List people = new List< Person>
{
    // object initializer:
    new Person { Age = 6, FirstName = "Andy", LastName = "Andrews", NumberOfChildren = 0 },
    new Person { Age = 46, FirstName = "Bob", LastName = "Bobbins", NumberOfChildren = 3 },
    new Person { Age = 19, FirstName = "Charlie", LastName = "Charles", NumberOfChildren = 0 }
};

Collection initialization is a shorter way of creating a collection and initialising it. Whereas before, the list would have to be created, and each item added separately, the whole thing can be done in one swoop.

Object initialization is very similar, and makes it easier to create an object and set property values at the same time.

Lambda Expressions

For the three Person instances added to the list, you could specify an anonymous method in C# 2.0 to extract items (in our case items of type Person) from the list based on a set of ‘matching criteria’:

List< Person> children = people.FindAll( delegate( Person p) { return p.Age < 18; } );

This is all well and good, but the problem with anonymous methods is that they're a little bit, well, clunky.

With lambda expressions, we can rewrite the above 'query' as follows:

    IEnumerable<Person> children =
                people.Select(p => p).Where(p => p.Age < 18);

Basically, we're referring to an item in the list as 'p', selecting 'p', and filtering using the Where method where the Age of the item is less than 18. At first, I found them a little strange syntactically, but I've got used to them now, and don't typically use the old style anonymous methods.

LINQ, var and anonymous type projection

LINQ is part of C# 3.0 and it's going to revolutionise the way that .NET developers access and manipulate data. The previous example showed how the Select and Where methods seem familiar to a SQL way of dealing with data.

I attended a DDD event last year, and was introduced to the 'var' keyword during a presentation. I'm guessing that it was new to my fellow developers present because there were several loud gasps from the attendees.

Aaaggggh! It's like the old days of variants!

var myName = "Shane";

The thing to really shout about here is that it is not a variant! The compiler simply infers the type from the data on the right side of the assignment, in this case, a string. It is a string! It is not a variant!

The reason that this keyword is introduced is for anonymous types, and the following example, with some new LINQ syntax thrown in for good measure, will illustrate:

var adults =
    from p in people
    where p.Age >= 18
    select new { Name = p.FirstName + " " + p.LastName, Age = p.Age }; // anonymous type projection

The first thing to note is that from, where and select keywords are exactly as in a SQL query, but that the from occurs first. This is mandatory, and it's unusual position is because Visual Studio's intellisense can only offer member information based on the data that is being queried. Because this is first, Visual Studio can offer help for the where clause and the select clause (which appears last.) There are a number of other methods and keywords, but I'll keep things brief here.

You'll notice that we're referring to an item in the list as 'p', referencing 'p' in the where clause with the Age property of the Person class, and then doing a select.

This select is doing something more interesting than the earlier example (which was just selecting people from the list). It is using anonymous type projection, whereby an anonymous type is created as a result of a select on the data.

The type doesn't have a name (therefore it's anonymous), and its properties can be specified 'on the fly'. Here, we're creating a new type to merge the first name and last name as 'Name', and creating an 'Age' property for the Person 'Age' property.

Because this type is anonymous, we need to specify the var keyword, and let the compiler work things out by inferring the type.

// local variable type inference:
foreach (var currentAdult in adults)
{
    Console.WriteLine(currentAdult.Name + ".  Their age is " +
          (currentAdult.Age.IsEven() ? "" : "not ") + "even");
}

Once again, type inference comes into play, and we loop through the selected adults. Note that the previously demonstrated int extension method is used (IsEven).

Remember that the Person class had a LogCreation partial method that was implemented? Well, that is called every time a Person is constructed, so for our collection, object initialization and output, we get the following:

Program output with partial method implementation

If the partial method implementation is taken out of the Person class, everything still compiles, but we get the following:

Program output with partial method implementation removed

So, that concludes my brief overview of new language features. Without doubt, the var keyword is likely to cause some controversy, but it really isn't bad, and is in fact required for anonymous types (which I believe to be a very handy addition to the language.)

I'm certainly looking forward to using the new language features, and I hope you do too.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.