The Least You Need to Know about C# 3.0

Jomo Fisher—A lot of people (myself included) have written about LINQ in the next version of C#. LINQ is indeed an empowering technology. However, even without LINQ, C# 3.0 would be a compelling upgrade. Now that Beta2 is publicly available, here’s my personal list of the most useful features in the next release:

You Can Target Older .NET Framework Versions

There are many reasons you might want your application to run on an older version of the frameworks. In Visual Studio 2005, this was very hard to accomplish. In Visual Studio 2008, there is now built-in support for this. Right-click on Project èPropertiesèApplication:

image

Once this is set, you’ll get a build error if you try to use a framework assembly from a more recent version. One critical thing here: all new C# 3.0 language features are available even if you’re targeting .NET 2.0 or 3.0. This means you will be able to use most new language features, but not LINQ itself because it requires supporting assemblies from the 3.5 framework.

Unfortunately, support wasn’t extended back to .NET Framework 1.1.

Automatic Properties

The automatic property feature is one of my favorite new features because it makes it easy create to immutable classes:

        class Point {

            public int X { get; private set; }

            public int Y { get; private set;}

            public Point(int x, int y) {

                X = x;

                Y = y;

            }

        }

Here, X and Y are true properties but the compiler is providing a hidden implementation of the backing field. This is a nice, succinct representation that allows you to modify the implementation of the properties if you need to later.

Simpler Delegates

I tend to use a lot of delegates while writing unittests because they can allow me to mock a function so that I can isolate a particular piece of code. In the 2.0 compiler, you would write a delegate like this:

 

Func<int, int> mydelegate = delegate(int x) { return x + 1; };

 

In C# 3.0, this can be simplified to:

 

Func<int, int> mydelegate = x => x + 1;

Going forward, the delegate keyword is still there for compatibility but there’s no reason to use it (except maybe if it seems more explicit to you). Notice also, that you don’t have to specify the type of ‘x’. In many cases, the compiler can figure out the type for you.

 

Type Inference: The var Keyword

You can now ask the compiler to try to figure out a type for you:

 

var name = "Bob";

The var keyword is not the same thing as object. The difference is that the compiler knows what the type of the variable name is. For example, this code will compile with no errors:

 

        public static int GetName() {

            var name = "Bob";

            return name.Length;

        }

 

If you replaced that var with object the code would not compile because there’s no Length method on the type object.

 

A possible downside is that when you’re reading through someone else’s code your brain has to do the type inference in order to understand what’s happening. To help with this, the editor gives you some help. Just hover your cursor over a particular var:

image

HashSet

The .NET framework finally has a built-in Set Collection. It’s called HashSet because Visual Basic has a Set keyword already.

 

Collection Initializers

There’s a new syntax for initializing collections:

 

var mystrings = new List<string> { "Alice", "Bob", "Charlie" };

 

The only requirement is that the collection type implements IEnumerable and the collection type has a suitable method called Add. The corresponding code in C# 2.0 is not terse:

 

List<string> mystrings = new List<string>();

mystrings.Add("Alice");

mystrings.Add("Bob");

mystrings.Add("Charlie");            

 

Extension Methods

Extension methods allow you to add methods to pre-existing types. This example adds a new method to the built-in string class.

     public static class MyStringMethods {
        public static string Reverse(this string s) {
            StringBuilder sb = new StringBuilder(s.Length);
            for (int i = s.Length - 1; i >= 0; --i) {
                sb.Append(s[i]);
            }
            return sb.ToString();
        }
    }

Unlike regular class methods, extension methods aren't allowed to access private and protected members of the class.

 

Expression Trees

I went back and forth about whether to include Expression Trees in this particular article. They’re not as simple to explain or understand as the rest of the stuff in this list. However, given that they’re probably the most important single feature I’m giving it a shot. Expression trees allow you to capture and manipulate the structure of a function at runtime. Consider this delegate that adds one to its input:

 

Func<int, int> myexpr = x => x + 1;

 

Now, a simple change allows you to get the function as an expression tree. Add Expression<> around the Func<int,int> like this:

 

Expression<Func<int, int>> myexpr = x => x + 1;

 

Now myexpr is a tree that represents the function. You can manipulate it, analyze it and compile it into a true delegate that you can execute. This powerful trick of making code and data the same thing is as old as LISP (which has been called the only computer language that is beautiful). That’s all I’m going to say here, but I do show some powerful ways to use this feature here and here.

 

Okay, that’s my list. I’ve excluded anonymous types because I haven’t found a truly compelling use for them separate from LINQ. If you have one I’d be very interested in hearing about it.

 

Honestly, the new 3.0 compiler has changed the way I work. So much so that I’m unhappy when I have to work on the VS 2005 machine I keep at home. I hope you’ll be as happy with C# 3.0 as I have been over the last year or so.

 

Update August-8, 2007--Here's another developer's perspective http://spellcoder.com/blogs/dodyg/archive/2007/08/08/7756.aspx that I found insightful.

 

This posting is provided "AS IS" with no warranties, and confers no rights.

 

kick it on DotNetKicks.com