C# 3.0

If you thought C# 2.0 was a big update, the 3.0 release was even bigger! It is difficult to do justice to it in a single chapter (let alone part of a chapter). So we are going to focus on the main features, especially as it relates to the evolution of C#.

First though, we should talk about the difference between C#, the CLR, and the .NET Framework. Up until now, they all mostly had the same version (that is C# 2.0, CLR 2.0, and .NET Framework 2.0), however, they released an update to the .NET Framework (3.0) that had no language or CLR changes. Then with .NET 3.5, they released C# 3.0. The following diagram explains these differences:

C# 3.0

Confusing, I know. Although both the C# language and the .NET Framework received an upgrade, the CLR remained unchanged. It is hard to believe, especially in light of all the new features, but it goes to show how forward thinking the developers of the CLR have been, and how well-engineered and extensible the C# language/compiler is that they were able to add new features without new runtime support.

Syntax updates

As usual, we will begin reviewing the syntactic changes of the language for this release. First are properties, which as you will remember are already an improvement over the old school getter and setter methods. In C# 3.0, the compiler can automatically generate the backing field for simple getters and setters as follows:

public string Name { get; set; }

This feature alone cuts out many lines of code from classes that have many properties. Another nice feature introduced is that of object initializers . Take the following simple class:

public class Person
{
    public string Name { get; set; }
    public int Age { get; set; }
}

If you want to create an instance and initialize it, you would normally have to write code as follows:

Person person = new Person();
person.Name = "Layla";
person.Age = 11;

But with an object initializer, you can do this at the same time as object instantiation as follows:

Person person = new Person { Name = "Layla", Age = 11 };

The compiler will actually generate pretty much the same code as before, so there is no semantic difference. But you can write your code in a much more concise and easy-to-read manner. Collections get a similar treatment as you can now initialize arrays with the following syntax:

int[] numbers = { 1, 2, 3, 4, 5 };

And dictionaries, which were notoriously verbose to initialize, can now be created very easily as follows:

Dictionary<string, int> states = new Dictionary<string,int>
{
    { "NY", 1 },
    { "FL", 2 },
    { "NJ", 3 }
};

Each of these improvements makes the very act of typing code easier and quicker. But it seems that the C# language designers were not content to stop there. Every time you instantiated a new variable, you were forced to write out the entire type name, when you start getting to complex generic types this can add a lot of extra characters to your program. Fear not! You do not even need to do that in C# 3.0! Look at the following code:

var num = 8;
var name = "Ashton";
var map = new Dictionary<string, int>();

As long as it is clear what type is being assigned on the right side of the equation, the compiler can take care of figuring out the type on the left side. Astute readers will no doubt recognize the var keyword from JavaScript. Though it looks similar, it is not the same at all. C# is still statically typed, which means that every variable must be known at compile time. The following code will not compile:

var num = 8;
num = "Tabbitha";

So, in effect, this is just a shortcut to help you type fewer characters, the compiler is just really good at inferring these things. In fact, that is not the only thing it can infer. If there is enough context, it can also infer generic type parameters. For example, consider the following simple generic method:

public string ConvertToString<T>(T value)
{
    return value.ToString();
}

When you call it, rather than stating the type in the call, the compiler can look at the type of class that is being passed in, and simply assume that this is the type that should be used for the type parameter as follows:

string s = ConvertToString(234);

At this point, I imagine someone on the C# language team said: "While we're making existing syntax optional, why not do away with the need for class definitions entirely!" And it would seem they did just that. If you need a data type to hold a few fields, you can declare it inline as follows:

var me = new { Name = "Joel", Age = 31 };

The compiler will automatically create a class that matches the type that you just created. There are a few limitations to this feature: you have to use the var keyword, and you cannot return an anonymous type from a method. Very useful when you are writing an algorithm and need a quick, yet complex data type.

All of these little syntax changes add up and make the C# language a pleasure to write in. They also are a lead in for the next big feature we are going to talk about.

LINQ

Language Integrated Query(LINQ) is the flagship feature of C# 3.0. It acknowledges the fact that much of a modern day program revolves around querying for data in one way or another. LINQ is a set of diverse features that gives the language first class support for querying data from a multitude of sources. It does so by providing a strong abstraction around the concept of querying, and then adding language support.

The C# language team started with the premise that SQL was already a great syntax for working with set-based data. But, unfortunately, it was not a part of the language; it required a different runtime, such as SQL Server, and only worked in that context. LINQ requires no such context switch, so you can simply get a reference to your data source, and query away.

Conceptually, there are the following, high level kind of operations that you can do with a set:

  • Filtering: This is performed where you exclude items from a set based on some criteria
  • Aggregation: This involves common aggregation actions such as grouping, and summation
  • Projection: This is extracting or converting items from a set

The following is what a simple LINQ query looks like:

int[] numbers = { 1, 2, 3, 4, 5, 6 };

IEnumerable<int> query = from num in numbers
                            where num > 3
                            select num;

foreach (var num in query)
{
    Console.WriteLine(num);
}
// outputs 4, 5, and 6

It looks like SQL, kind of. There have been many questions over the years over why the syntax does not start with the select statement like it does in SQL, but the reason comes down to tooling. When you start typing, they want you to be able to get IntelliSense when typing every part of the query. By starting with the 'from', you are essentially telling the compiler what type will be used in the rest of the query, which means it can give you type-time support.

One of the interesting things about LINQ is that it works for any IEnumerable. Think about that for a second, every single collection in your program is now easily searchable. And that is not all, you can aggregate and shape the output as well. For example, say you wanted to get a count of cities in each state as follows:

var cities = new[]
{
    new { City="Orlando", State="FL" },
    new { City="Miami", State="FL" },
    new { City="New York", State="NY" },
    new { City="Allendale", State="NJ" }
};

var query = from city in cities
            group city by city.State into state
            select new { Name = state.Key, Cities = state };

foreach (var state in query)
{
    Console.WriteLine("{0} has {1} cities in this collection", state.Name, state.Cities.Count());
}

This query uses the group by clause to group the values by a common key, in this case by state. The final output is also a new anonymous type that has two properties, the name, and the collection of cities in that state. Running this program will output this for Florida as FL has 2 cities in this collection.

So far in these examples, we have been using what is called query syntax . This is nice because it is very familiar to those who know SQL. However, just as with SQL, more complex queries can sometimes get rather verbose and complicated to read. There is another way to write LINQ queries that, for some, can be much easier to read, and perhaps even slightly more flexible called the LINQ method syntax, it is built upon another new feature of the language.

Extension methods

Normally, the only way of extending functionality of a type is to inherit from the class and add the features to the subtype. All users have to use the new type to get the benefits of that new type. However, this may not always be an option, for example, if you are using a third-party library with value types (as you cannot inherit from a value type). Let us say we have the following struct in a third-party library, where we do not have access to modify the source code:

public struct Point
{
    public float X;
    public float Y;
}

With extension methods, you have the capability to add new methods to this type as follows:

public static class PointExtensions
{
    public static void Add(this Point value, Point other)
    {
        value.X += other.X;
        value.Y += other.Y;
    }
}

Extension methods must be placed in a public static class. The method itself will be static, and will use the this keyword on the first parameter to signify the type to attach to. Using the previous method looks like the method has always been a part of the type as follows:

var point = new Point { X = 28.5381f, Y = 81.3794f };
var other = new Point { X = -2.6809f, Y = -1.1011f };

point.Add(other);
Console.WriteLine("{0}, {1}", point.X, point.Y);
// outputs "25.8572, 80.2783"

You can add the extension methods to any type, whether value type, or reference type. Also interfaces and sealed classes can be extended. If you look at all of the changes in C# 3.0, you will notice that you are now writing less code because the compiler is generating more and more of it behind the scenes for you. The result is code that looks similar to some of the other dynamic languages such as JavaScript.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.12.107.31