Three Cool New Features in C#

Three Cool New Features in C#

# has always had a reputation as a clean language with lots of innovation. The Whidbey-release of Visual Studio .NET ships with a new version of the C# compiler that has a number of great new features. Some of them will be implemented as generic runtime features that will show up in other languages as well, and some are truly C#-specific. All of them originated in the C# camp.

Anonymous Methods, Generics, and Partial Types are among the new features that will enter the C# arena in the near future. Some have been long anticipated; others are a surprise. All of them increase productivity and code reuse.

Anonymous Methods
If you have developed in C#, you are probably familiar with Delegates. Delegates are objects that encapsulate references to functions. One of the most common uses of Delegates is the implementation of event handler code. To see this in action, create a new Windows Forms (WinForms) project, and drop a button onto your form. Then, double-click on the button to create code that handles the click event. Behind the scenes, the WinForms designer creates two separate pieces of code. There is the actual event handler code, which, after adding the messagebox call, might look like this:

   private void button1_Click(      object sender, System.EventArgs e)   {     MessageBox.Show("Test");   }

Additionally, and usually hidden in the form’s designer-generated code region, this code is wired up to the button’s click event like so:

   this.button1.Click +=       new System.EventHandler(      this.button1_Click);

Note that the event handler method has to conform to a certain signature (two parameters of type object and System.EventArgs in this case). This is defined in the System.EventHandler delegate.

The big question is: What did we gain by creating this separate method that is tied to the click event through the EventHandler delegate? Well, not a whole lot, because there is no true need to call the method otherwise. But all of this is necessary in order to make event handling work.

Anonymous Methods simplify this a bit. Rather than instantiating a delegate and creating a method to point to, the code for the whole handler method can be created inline. No method name is defined (hence the name Anonymous Methods), but parameters are still required. Here’s what that syntax looks like:

   this.button1.Click +=    delegate(object sender, EventArgs e)     {    MessageBox.Show("Test");   };

As you can see, what is assigned to the Click event is not a pointer to a method, but the entire method code itself, including the parameters, and without a method name. Note that there has to be a semi-colon at the end, because all of this is really just one line of code.

One of the really cool uses of this technique is the ability to pass code as a parameter. Envision a scenario where a delegate is used as a call-back, a pattern that is commonly used in a number of scenarios, such as asynchronous programming, or whenever a process reports on its progress. Here is an example that illustrates this:

   public delegate void      Feedback(string Text);      public void Execute(Feedback d1)   {     d1("Starting...");   // More code     d1("Still going...");   // Mode code     d1("Done.");   }

You could now instantiate a delegate that encapsulates a method with one string parameter and pass it to the Execute method to receive feedback on the method’s progress. Alternatively, you could just call the method and pass the feedback code in as a parameter:

   Execute(      delegate(string s)      {MessageBox.Show(s);} );

So everything within the parenthesis of the Execute() call is the anonymous method. And of course, you can pass in as many lines of code as you want. Oh, and before I forget, unlike manually created delegates, anonymous methods can actually see local variables defined in the calling method. So this works just fine:

   string s2 = "Test";   Execute(      delegate(string s)      {MessageBox.Show(s+s2);} );

Partial Classes
In general, classes are defined in a single source file. Sometimes, this can lead to difficulties. One example is a class that is partially generated by a code generator, and partially modified and maintained by the developer. This is a very common scenario. Both WinForms and Web Forms designers encounter this issue, making these classes vulnerable to accidental modification of source code that is not meant to be modified by the developer. To solve this problem, the WinForm designer-generated code is hidden in a region and never touched at all, or only touched by very skilled developers.

The new version of C# introduces a feature that can solve these problems: partial classes. These are classes that are defined in more than one source file. This way, part of a partial class could be maintained by the code generator and a second part of the class could be maintained by the developer. When the code gets compiled, the C# compiler performs what can be described as a fancy text merge operation that combines all parts into one class.

Here’s a code sample. First, there could be one part of a class defined in one source file:

   public partial class Class1   {      public string Test()      {         return "Test";      }   }

Then, there could be a second source file with the following class definition:

   public partial class Class1   {      public string SomethingElse()      {         return "Something Else";      }   }

The significant part is the definition of the class. First of all, both class definitions sport the same class name. Secondly, there is the partial keyword, which makes the duplicate class name a valid construct, and instructs the compiler to merge the two parts together.

Note that all (there could be any number, really) class parts combined must create a valid class. In other words, it would be invalid if multiple parts defined the same exact methods, as that would result in a duplicate method name. The same is true for fields, properties, constructors, and so forth. The parts of the class are merged together on class level. Therefore, multiple methods of the same name are not merged into one large method. Similarly, a method defined in one part cannot inherit functionality defined in another part. Once again, this is really mostly a text-merging feature, and has no impact on the inheritance functionality native to C# and Visual Studio .NET in general.

What is interesting is that partial types are fully integrated into the Visual Studio .NET IDE. Type “this.” in any of the parts, and you will see full IntelliSense functionality, including the members defined in the other part(s).

Of course, this merge-awareness can be useful for a number of different practical applications. Code generators are one of them. Also, partial classes can be used to logically break down large classes into smaller bits, which can be very useful in team development scenarios., This can be a handy feature when it comes to adding debug functionality to a class. One of the parts could have all the debug functionality, and depending on the desired behavior, the whole debug part could be removed when you’re ready to go live.

All Visual Studio .NET languages, including C# of course, are strongly typed. This means that pretty much everything you create has to be of a certain type. This is great for quality assurance, and in general, is a much desired feature. Unfortunately, there is a bit of a side effect as well. It is very difficult to create generic constructs without giving up type safety. The only way to create code in Visual Studio .NET that can handle any type is by making the utilized reference into an Object. Everything in Visual Studio .NET inherits from the Object class at some level. Therefore, everything in .NET is an object and can be typed to be an object. This is true from the simplest data types, such as an integer, all the way up to the heavy hitters such as the DataSet object.

So let’s take a look at a specific sample. Let’s say you want to create a collection object that can store references to any type of object. That collection object could have the following methods:

   public class MyCollection   {      public void Add(object o1)      {         // Store the object      }      public object GetByIndex(int i)      {         // Retrieve the object         return null;       }   }

As you can see, this is a simplified code example and not a true collection, but I think it is sufficient to illustrate the issue. Using this construct, you can now store any type in the collection, and retrieve it later. For instance, you could store an integer or a DataSet:

   MyCollection Col1 =       new MyCollection();   Col1.Add(1);   Col1.Add(new DataSet());

The compiler knows that both the DataSet and the integer (1) inherit from “object” and therefore, the parameter types are valid. It gets a bit trickier when you retrieve these types again, because the return type is always “object.” If you want to get at the original types, you have to perform a cast:

   DataSet ds2 =      (DataSet)Col1.GetByIndex(0);

As you may have noticed, there is a bit of an issue here! Performing the cast to type DataSet is a runtime operation. The compiler cannot know whether or not that case will succeed at runtime. And in fact, in this example, it would not be successful, as the item with index 0 is an integer and not a DataSet.

Another problem can be performance. Integers, for instance, are value types. And object (as well as the DataSet) is a reference type. This means that whenever the runtime casts from an integer to an object and back, operations known as boxing and unboxing have to be executed. This means that the value type is taken from the stack, packaged into an object, and put on the heap. This is not an operation you want to use just to store a reference.

So clearly, there is an issue here as type safety is not maintained. At the same time, you really would like a generic collection class because storing object references in this manner is a rather common task. And creating specific collection classes for every possible type that occurs in the .NET Framework, as well as your own code, is not feasible either. You’d be doubling the number of used classes on the spot!

Generics solve these issues. They allow creating code that is as generic as the collection above, but when the code is used, a specific type is assumed. Here’s the redefined collection using Generics:

   public class MyCollection   {   public void Add(myType o1)      {   // ...      }   public myType GetByIndex(int i)      {   // ...      }   }

In this example, the whole class is defined with a parameter called myType. This myType parameter (there could be more than one) is used to define the parameter type as well as the return type. Of course myType is not defined anywhere. Instead, the code that uses this class defines the “real” type that is to be used. For instance, if you want to use this collection to store integers, you use it like so:

   MyCollection Col1 =       new MyCollection();   Col1.Add(1);

Internally, the collection now operates with integers. There is no boxing and unboxing. Also, values retrieved from the collection are integers without having to be converted. And on top of that, this instance of the collection is limited to storing integers. An attempt to assign a DataSet is trapped as an error during compilation. You could easily use the whole collection for DataSets as well:

   MyCollection Col1 = new      MyCollection();

It is worth mentioning that any code using Generics can also apply a filter (constraint), so only a specific list of interfaces or classes can be used as the generic parameter. This is especially useful when the code inside the method that handles the Generic performs more complex operations than are supported by the object type. When no constraints are defined, there is no way for the compiler to know how the Generic is going to be used. Therefore, the lowest common denominator has to be assumed, and that, once again, is the object.

As a side-note, Generics are a CLR (runtime) feature and therefore not truly specific to C# (although that’s where they originated). So other languages, like Visual Basic .NET, (can) support Generics as well.

The Whidbey-release of C# adds a number of great features, cementing the position of C# as an innovative and modern language. Some of the features are not entirely new and unheard of, but they are, nevertheless, very useful. Note also that some of these features require changes in the runtime (CLR), which means that they are available to other .NET languages as well, if they chose to support them.


About Our Editorial Process

At DevX, we’re dedicated to tech entrepreneurship. Our team closely follows industry shifts, new products, AI breakthroughs, technology trends, and funding announcements. Articles undergo thorough editing to ensure accuracy and clarity, reflecting DevX’s style and supporting entrepreneurs in the tech sphere.

See our full editorial policy.

About Our Journalist