Home > C# > An Overview of C# 4.0

An Overview of C# 4.0

C# Sharp 4.0 features Kevin Jones

December 1rst | 2009

An Overview of C# 4.0

In Some Love for VB.NET 10 Too I focused on the new features in VB.NET 10. Now let’s take a look at C# 4.0. C# already has a strong, rich feature set which will, no doubt, be developed even further. I will discuss the actual C# 4.0 specification itself, not the .NET Framework, so the ever-popular “dynamic” keyword will be left for later. It deserves a post of its own.

COM Cleanup

This feature is pretty cool and it’s definitely going to get a big cheer from those who do a lot of COM automation. C# 4.0 has a few extra features that make working with COM a little easier. Word automation is a popular reason to use COM automation in the .NET Framework. Take this example which runs Word for you and adds a paragraph with bold text:

object oMissing = System.Reflection.Missing.Value;
var word = new Application();
word.Visible = true;
var doc = word.Documents.Add(ref oMissing, ref oMissing, ref oMissing, ref oMissing);
var paragraph = doc.Content.Paragraphs.Add(ref oMissing);
paragraph.Range.Text = "Hello World!";
paragraph.Range.Bold = 1;
paragraph.Range.InsertParagraphAfter();
Console.ReadKey(true);

Something that immediately stands out is all those ref’s to oMissing. What is that? Simply put, C# doesn’t support optional parameters, but COM (and VB.NET) do. To indicate to the COM Callable Wrapper that you want it to be treated like “nothing” was passed in (as opposed to NULL) Missing.Value is required. In C# 4.0, that is no longer the case. Here it is in C# 4.0:

var word = new Application();
word.Visible = true;
var doc = word.Documents.Add();
var paragraph = doc.Content.Paragraphs.Add();
paragraph.Range.Text = "Hello World!";
paragraph.Range.Bold = 1;
paragraph.Range.InsertParagraphAfter();

Much better! What’s actually happening here is just syntactic sugar. The C# compiler automatically adds a ref to Missing.Value for you, which is easy to see with a tool like Reflector. If you want to pass in Missing.Value automatically from some parameters, and actual objects for others, read the section on named parameters farther down in this blog. In addition to adding Missing.Value for you, you can now—optionally—not pass a parameter by reference even if the signature declares it as ref.

A new addition to the C# 4.0 language is support for indexed properties. For example, let’s look at some code that adds a bookmark to the document:

object oMissing = System.Reflection.Missing.Value;
object endOfDoc = "\\endofdoc";
var word = new Application();
word.Visible = true;
var doc = word.Documents.Add(ref oMissing, ref oMissing, ref oMissing, ref oMissing);
var paragraph = doc.Content.Paragraphs.Add(ref oMissing);
paragraph.Range.Text = "Hello World!";
paragraph.Range.Bold = 1;
paragraph.Range.InsertParagraphAfter();
var bookmark = doc.Bookmarks.get_Item(ref endOfDoc);
bookmark.Range.Text = "Goodbye World!";

bookmark is declared as doc.Bookmarks.get_Item(). Bookmarks is actually a property that supports indexing. C# 3.0 doesn’t support it. In C# 4.0 we can now index into it:

object oMissing = System.Reflection.Missing.Value;
object endOfDoc = "\\endofdoc";
var word = new Application();
word.Visible = true;
var doc = word.Documents.Add(ref oMissing, ref oMissing, ref oMissing, ref oMissing);
var paragraph = doc.Content.Paragraphs.Add(ref oMissing);
paragraph.Range.Text = "Hello World!";
paragraph.Range.Bold = 1;
paragraph.Range.InsertParagraphAfter();
var bookmark = doc.Bookmarks[endOfDoc];
bookmark.Range.Text = "Goodbye World!";

Neat. Does this mean C# 4.0 now supports indexable properties? Unfortunately not. As of now it can only consume them, not declare them—but it’s a step in the right direction. Also notice that we didn’t pass the endOfDoc by reference. With COM, you no longer have to pass an item by reference if you prefer not to. This only works on COM wrappers and not regular .NET managed code.

This brings us to the next feature of COM: embedded interop assemblies. A problem that COM presents for some developers, which .NET developers have to deal with when working with COM, is versioning—or making sure that the required interop wrapper is even installed. In .NET 4.0, the compiler will automatically embed your COM interop assemblies into your application if you simply right-click the reference and hit properties. This will prevent your compiled application from referencing the assembly. Instead, it will contain its own types. This ensures that your application is always using the same interop wrapper.

So our C# 3.0 code went from this:

object oMissing = System.Reflection.Missing.Value;
object endOfDoc = "\\endofdoc";
var word = new Application();
word.Visible = true;
var doc = word.Documents.Add(ref oMissing, ref oMissing, ref oMissing, ref oMissing);
var paragraph = doc.Content.Paragraphs.Add(ref oMissing);
paragraph.Range.Text = "Hello World!";
paragraph.Range.Bold = 1;
paragraph.Range.InsertParagraphAfter();
var bookmark = doc.Bookmarks.get_Item(ref endOfDoc);
bookmark.Range.Text = "Goodbye World!";

To This:

var word = new Application();
word.Visible = true;
var doc = word.Documents.Add();
var paragraph = doc.Content.Paragraphs.Add();
paragraph.Range.Text = "Hello World!";
paragraph.Range.Bold = 1;
paragraph.Range.InsertParagraphAfter();
var bookmark = doc.Bookmarks["\\endofdoc"];
bookmark.Range.Text = "Goodbye World!";

That’s cleaner now, huh?

Covariance and Contra-variance

This new feature was added to C# 4.0 to help with generics. In today’s C# code, all generics are invariant. For example, given the type SomeType<T> and SomeType<K> let’s assume that K is a superclass of T. SomeType<T> and SomeType<K> will not have any inheritance relationship at all even though T and K have a relationship. Why can’t we do this?

Ensuring type safety. In this example, allowing this to occur falls short:

List<string> stringList = new List<string>();
List<object> objectList = stringList;
objectList.Add(new object());

Even though string inherits from object, we can’t assign it to a list of objects. Line three is a good example of why not: our objectList would really be a list of strings at run time, then blam! we add an object to it. Delegates have the same problem.

private delegate T Callback<T>();
public void DoCallback()
{
    Callback<object> callback = new Callback<string>(CallbackHandler);
}

private string CallbackHandler()
{
    //Implementation Omitted
}

You would assume that this is OK. After all, why shouldn’t a callback of object be assigned to a callback of string if string always implements object? Well…for the same reason as the List.

Variance to the rescue! Where have we seen variance before? Arrays. This has always compiled:

object[] actuallyStrings = new string[] { "Hello", "World" };
actuallyStrings[0] = new object();

This will throw an ArrayTypeMismatchException, but the compiler allows it. With generics, we can do this in a way that is always compile time safe. If a generic interface or generic delegate has a reference type T as its type parameter and does not have any method or member that takes in a parameter of type T, we can declare it to be covariant on T. On the other hand, if that interface or delegate does not have any method or member that returns T, we can declare it to be contravariant on T.

NOTE: That description was taken from Buu Nguyen’s post on Code Project. It’s an elegant description and kudos to him for thinking of it.

Thus, in C# 4.0 we can declare on our delegate that T is out as we never accept a type of T.

private delegate T Callback<out T>();
public void DoCallback()
{
    Callback<object> callback = new Callback<string>(CallbackHandler);
    object result = callback();
}
private string CallbackHandler()
{
    return "Hello";
}

result in this case, is an object and rightfully so. Since T can only be an output type, and we know string always inherits from object, the compiler can safely assume the implicit cast.

So why is this a feature in C# 4.0? As Buu points out, this feature has actually been available to the CLR since generics were introduced in .NET 2.0, though none of the languages supported it.

Named and Optional Parameters

Named and default parameters partially complete the COM interop enhancements—something that many consider long overdue. With named parameters, you can specify the order in which your parameters are declared. This also comes into play with option parameters. Here is an example:

static void Main(string[] args)
{
    PrintTwoStrings(stringB: "World", stringA: "Hello");
}

static void PrintTwoStrings(string stringA, string stringB)
{
    Console.WriteLine(stringA);
    Console.WriteLine(stringB);
}

Notice that I explicitly declared the parameters, even out of order. Likewise, I can make stringB optional with a default. This code will produce “Hello Japan”

static void Main()
{
    PrintTwoStrings("Hello", stringB: "Japan");
}

static void PrintTwoStrings(string stringA, string stringB = "World")
{
    Console.WriteLine(stringA);
    Console.WriteLine(stringB);
}

Even though stringB has a default, we explicitly set it to Japan. If we were to omit the stringB in the call to PrintTwoStrings:

static void Main()
{
    PrintTwoStrings("Hello");
}

This will print “Hello World”. You can have multiple optional parameters, but they must all be placed after the required ones. This is to avoid confusion with overloads.

This also works with COM. If you have a COM method that takes an exceptional number of arguments, and you want to pass in Missing.Value for all but a few, you can use named parameters and it will default all of the others to Missing.Value.

It’s worth noting that C# and VB.NET are compatible with each other on named and optional parameters. VB.NET has actually supported both these features for years.

Kevin Jones is a Team Lead at Thycotic Software, an agile software services and product development company based in Washington DC. Secret Server is our flagship password management software product. On Twitter? Follow Kevin

  1. anonymous
    December 4, 2009 at 2:10 am

    Nice text but a bit long:)

  2. December 6, 2009 at 8:07 pm

    Nice overview. I’m a fan of all the syntatic sugar that has been added. Although i’m not sure how I feel about optional parameters.

  3. December 7, 2009 at 4:04 pm

    @anonymous: Thanks for the feedback. I got a bit excited with all of the new cool stuff! Another one will be coming up soon covering the “dynamic” keyword that I skipped out on. They were originally one post 🙂

  4. December 30, 2009 at 7:16 am

    hi,

    First of all. Thanks very much for your useful post.

    I just came across your blog and wanted to drop you a note telling you how impressed I

    was with the information you have posted here.

    Please let me introduce you some info related to this post and I hope that it is useful

    for .Net community.

    There is a good C# resource site, Have alook

    http://www.csharptalk.com

    simi

  5. tecnodude
    April 26, 2010 at 4:03 am

    Hi,

    Can you please tell me how to

    * add bookmark 7cm from the top of the page?
    * add bookmarks in series one after the other add text to those bookmarks.

    appreciate it…

  6. June 15, 2010 at 4:54 am

    thanks for sharing,,

  1. December 4, 2009 at 6:09 pm
  2. December 21, 2009 at 11:44 am
  3. February 28, 2010 at 12:10 pm

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: