0% found this document useful (0 votes)
56 views9 pages

New Language Features in CSharp

The document discusses new features in C# 2.0 including anonymous methods, iterators, and partial classes. Anonymous methods allow declaring methods inline without an explicit class declaration. Iterators make it easier to create classes that support enumeration by using the yield return keyword. Partial classes allow splitting a single class across multiple files.

Uploaded by

myindex19
Copyright
© Attribution Non-Commercial (BY-NC)
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
56 views9 pages

New Language Features in CSharp

The document discusses new features in C# 2.0 including anonymous methods, iterators, and partial classes. Anonymous methods allow declaring methods inline without an explicit class declaration. Iterators make it easier to create classes that support enumeration by using the yield return keyword. Partial classes allow splitting a single class across multiple files.

Uploaded by

myindex19
Copyright
© Attribution Non-Commercial (BY-NC)
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
You are on page 1/ 9

New Language Features in C# 2.

0, Part 1
Four years ago, a new upstart language named C# surprised the development world with its
elegance and consistency. Now that Microsoft has released a technology preview version of
Visual Studio 2005 (formerly codenamed Whidbey), .NET's favorite language is back with
some new innovations. In this two-part series, you'll get a first look at three of the four major
language refinements in the latest version of C#.

Anonymous Methods

In .NET programming, every executable line of code takes place inside of a method, and
every method is explicitly declared at the class level -- until now. In C# 2.0, you can declare a
method inside of another piece of code, where a delegate is expected. This embedded method
is called an anonymous method. (Anonymous methods will look familiar to you if you've used
lambda functions in the Lisp programming language.)

Anonymous methods are primarily useful if you want to quickly hook up an event handler.
For example, here's the standard way to hook up an event handler that responds to the click of
a button (in any version of .NET):

public partial class Form1 : Form


{
// This method connects the event handler.
public Form1()
{
InitializeComponent();
button1.Click += new EventHandler(ButtonClick);
}

// This is the event handling method.


private void ButtonClick(object sender, EventArgs e)
{
MessageBox.Show("You clicked the button.");
}
}

In this example, there are two methods. Form1() initializes the window and connects the
event handlers. ButtonClick() receives the event when the user clicks the button.

Here's how you could rewrite this example using an anonymous method:

public partial class Form1 : Form


{
public Form1()
{
InitializeComponent();

button1.Click += delegate(object sender, EventArgs e)


{
// The following code is part of an anonymous method.
MessageBox.Show("You clicked the button, and " +
"This is an anonymous method!");
};
}
}
Notice that the event handler method is defined inside of Form1(), and is attached directly to
the Click event. The anonymous method starts with the keyword delegate, followed by the
parameter list and then the actual method code. Essentially, the Form1() method defines a
new method, but doesn't execute it. (In this example, the code in the anonymous method won't
run until the Click event fires.)

When you declare an anonymous method, the C# compiler has some liberty to decide how it
handles the situation. In this example, the compiler creates a new, uniquely named method in
the Form1 class. This anonymous method has access to all the public and private member
variables and properties of the Form1 class.

Even more interestingly, the anonymous method also has access to local variables defined in
the procedure where the anonymous method is declared. Here's an example that shows this
somewhat counterintuitive ability:

public partial class Form1 : Form


{
public Form1()
{
InitializeComponent();

string tempVar = "TEST";


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

To make this work, the C# compiler handles this code a little differently. It actually creates a
new class that it nests inside of the Form1 class. It adds the anonymous method to the new
class, along with a private member variable to store the contents from tempVar.

You don't need to attach an anonymous method to an event. You can also declare an
anonymous method and assign it to a delegate variable. This technique is useful if you need to
pass a delegate as a callback, or if you want to attach the same anonymous method to two
event handlers, as shown here:

public partial class Form1 : Form


{
public Form1()
{
InitializeComponent();

// Declare the delegate that points to an anonymous method.


EventHandler clickHandler =
delegate(object sender, EventArgs e)
{
MessageBox.Show("You clicked the button, and " +
"This is an anonymous method!");
};

// Attach the delegate to two events.


button1.Click += clickHandler;
button2.Click += clickHandler;
}
}
Overall, anonymous methods make most sense if your method code is relatively short;
otherwise, it can be difficult to distinguish the embedded anonymous method code from the
surrounding method code. One reason you might want to use anonymous methods is to
simplify the handling of asynchronous events. Using an anonymous method, you can define
how you want to handle the completion of an asynchronous task in the same context where
you start the asynchronous task. This approach can clarify your code.

Iterators

Often, developers need to create classes that support enumeration. When a class supports
enumeration, you can use the convenient foreach syntax to step through a group of items.
For example, if you create an OrderCatalog class that contains a group of OrderItem
instances, you might want to enumerate over all of the items using this code:

foreach (item OrderItem in catalog)


{
// (Process OrderItem here.)
}

This code gets translated by the C# compiler to use the GetEnumerator() method of the
class. Technically, it looks like this:

Enumerator e = catalog.GetEnumerator();
while (e.MoveNext())
{
OrderItem item = e.Current;
// (Process OrderItem here.)
}

As a result, this pattern only works with classes that implement the IEnumerable interface.
Creating a class that supports enumeration in C# 1.0 ranges from mildly inconvenient to
annoyingly awkward. In C# 2.0, a new language feature known as iterators makes it much
easer to create classes that support enumeration. Instead of building a state machine to keep
track of your position in a collection, you create one public method named GetEnumerator()
that returns all of the values directly using the new yield return keyword.

For example, here's a simple class that yields three pieces of information:

public class DecoratingColors


{
public string WallPaint;
public string Carpet;
public string Furniture;
public IEnumerator<string> GetEnumerator()
{
yield return WallPaint;
yield return Carpet;
yield return Furniture;
}
}

When you use foreach on a DecoratingColors instance, you'll wind up with three strings,
one after the other.
To see the real rewards of iterators, you need to consider a more realistic example. The
following code shows the OrderCatalog class, which contains a private collection of
OrderItem instances. In this example, the GetEnumerator() method traverses all the items in
the private collection in a loop, returning one item each time.

public class OrderCatalog


{
private ArrayList orderItems = new ArrayList();

public void Load()


{
// Fill collection for a test.
orderItems.Clear();
orderItems.Add(new OrderItem("Item 1"));
orderItems.Add(new OrderItem("Item 2"));
orderItems.Add(new OrderItem("Item 3"));
}

public IEnumerator<OrderItem> GetEnumerator()


{
foreach (OrderItem item in orderItems)
{
yield return item;
}
}
}

public class OrderItem


{
private string name;

public string Name


{
get { return name; }
}

public OrderItem(string name)


{
this.name = name;
}
}

Here's the code that puts this example to the test:

OrderCatalog catalog = new OrderCatalog();


catalog.Load();
foreach (OrderItem item in catalog)
{
MessageBox.Show(item.Name);
}

The beauty of iterators is that the enumerable class can concentrate on providing information,
while the consumer can concentrate on retrieving it. Neither part needs to worry about the
actual implementation details of how positioned is maintained.

Partial Classes
Partial classes give you the ability to split a single class into more than one source code (.cs)
file. Here's what a partial class looks like when it's split over two files:

// Stored in file MyClass1.cs


public partial class MyClass
{
public MethodA()
{...}
}

// Stored in file MyClass2.cs


public partial class MyClass
{
public MethodB()
{...}
}

When you build the application, Visual Studio .NET tracks down each piece of MyClass and
assembles it into a complete, compiled class with two methods, MethodA() and MethodB().

Partial classes don't offer much in the way of solving programming problems, but they can be
useful if you have extremely large, unwieldy classes. (Of course, this might be a sign that you
haven't properly factored your problem, in which case you should really break your class
down into separate classes.) The real purpose of partial classes in .NET is to hide
automatically generated designer code.

For example, if you build a .NET form in Visual Studio 2005, your event- handling code is
placed in the source code file for the form, but the designer code that creates and configures
each control and connects the event handlers is nowhere to be seen. In order to see this code,
you need to select Project-->Show All Files from the Visual Studio menu. When you do, a
new file appears with the other half of the class. For example, if you create a new form named
Form1, you'll actually wind up with a Form1.cs file for your code and a Form1.Designer.cs
file that contains the automatically generated part.

Summary
In the next article in this series, you'll learn about the sexiest new language feature in C# (and
no, it isn't multiple inheritance). In the meantime, for a more detailed look at the C# language
rules, surf to the C# language page at MSDN, at msdn.microsoft.com/vcsharp/team/language.
You'll find a link that allows you to download a detailed white paper on the new C# language
specifications, along with useful insight from the C# language designers in the "Ask a
Language Designer" section of the site.

New Language Features in C# 2.0, Part 2


The first part of this series introduced three new C# language features: anonymous methods,
iterators, and partial types. In this second part, we'll tackle the last and most exciting new
feature, Generics.

Understanding Abstraction

In a strongly typed programming language like C#, you have two choices for manipulating
objects:

 Use the most specific type possible. For example, if you want to manipulate customer
information, use references of type Customer.
 Use a more generic type. This might be a base class or an interface that more than one
class shares. For example, if you need to manipulate LocalCustomer and
PremiumCustomer references, treat them both in the same way by using the ICustomer
interface.

Usually, the first approach gives you the most control because you can access every aspect of
the object. However, the second approach has a different advantage. It lets you write generic
logic that can be used with different types of objects, reducing the total lines of code you need
to write and the complexity of your solution. In programming speak, your code attains a
higher level of abstraction, which makes it easier to reuse and maintain.

The problem with programming generically is that sometimes there isn't a useful base class or
interface that's shared by all the types you want to support. Usually, the only solution is to
build a weakly typed class that treats everything as the base type Object.

The perfect example of this problem is the System.Collections.ArrayList class. ArrayList


is an all-purpose dynamically self-sizing collection. It can hold ordinary .NET objects or your
own custom objects. In order to support this, ArrayList treats everything as the base Object
type. The problem is there's no way to impose any restrictions on how ArrayList works. For
example, if you want to use the ArrayList to store a collection of Customer objects, you have
no way to be sure that a faulty piece of code won't accidentally insert strings, integers, or any
other type of object. These mistakes won't raise an exception, although they might lead to
more subtle and insidious errors later on. As a result, one of the most common ingredients in a
.NET application is custom collection classes that derive from ArrayList but are strongly
typed. The .NET class library is swamped with dozens upon dozens of strongly typed
collections classes.

C# 2.0 finally offers a solution to this problem with a new language feature called Generics.

Generics 101
In C# 2.0, you avoid these headaches by creating classes that are parameterized by type. In
other words, you create a class template that supports any type. When you instantiate that
class, you specify the type you want to use, and from that point on, your object is "locked in"
to the type you chose. (Behind the scenes, .NET actually creates a new strongly typed class
dynamically at runtime.)

To see generics in action consider the following example, which shows the template for a
typesafe ArrayList:

public class ObjectList<ItemType> : CollectionBase


{
private ArrayList list = new ArrayList();
public int Add(ItemType value)
{
return list.Add(value);
}

public void Remove(ItemType value)


{
list.Remove(value);
}

public ItemType this[int index]


{
get
{
return (ItemType)list[index];
}
set
{
list[index] = value;
}
}
}

The ObjectList class wraps an ordinary ArrayList. However, it provides strongly typed Add()
and Remove() methods, along with a strongly typed indexes. These members use the type
ItemType, which is really just a placeholder for whatever type you choose when you create
the class. This placeholder is defined in angled brackets after the name of the class.

Here's an example of how you could use the ObjectList class to create an ArrayList collection
that only supports strings:

// Create the ObjectList instance, and


choose a type (in this case, string).
ObjectList<string> list = new ObjectList<string>();

// Add two strings.


list.Add("blue");
list.Add("green");

// The next statement will fail because it has the wrong type.
// In fact, this line won't ever run, because the compiler
// notices the problem and refuses to build the application.
list.Add(4);

Generics is a feature that's supported across all first-class .NET languages, including VB
.NET (unlike the other C# language features). Incidentally, the .NET Framework designers
are well aware of the usefulness of generic collections, and they've already created several
that you can use. You'll find them in the new Systems.Collections.Generic namespace.
Almost every type in this namespace duplicates a type from the System.Collections
namespace. The old collections remain for backward compatibility.

Advanced Generics

There's no limit to how many ways you parameterize a class. In the ObjectList example,
there's only one type parameter. However, you could easily create a class that works with two
or three types of objects, and allows you to make both of these types generic. To use this
approach, just separate each type with a comma (in between the angle brackets at the
beginning of a class).

For example, consider the following ObjectHashTable class. It allows you to define the type
you want to use for the items you are storing (ItemType) and the keys you are using to index
types (KeyType).

public class ObjectHashTable<ItemType,


KeyType> : DictionaryBase
{ ... }

Another important feature in generics is the ability to use constraints, which can restrict what
types are allowed. For example, you might want to create a class that supports any type that
meets a certain interface. In this case, just use the new where keyword.

Here's an example that restricts the ObjectList class so that it can only use serializable items.
(One reason you might use this approach is if you want to add another method in ObjectList
that requires serialization, such as a method that writes all the items to a stream.)

public class ObjectList<ItemType> : CollectionBase


where ItemType : ISerializable
{ ... }

You can define as many constraints as you want, as long as you separate them with commas.
Constraints are enforced by the compiler.

Incidentally, generics don't just work with classes. They can also be used in structures,
interfaces, and delegates. In fact, you can even use generics to parameterize a method by type,
as shown here:

public ItemType GenericFunction<ItemType>(ItemType


item)
{
// (Process item here.)
return item;
}

The code that calls GenericFunction() defines the type, which is then used for a parameter
to the function and a method.

string input = "...";


string output = Obj.GenericFunction<string>(input);
This technique isn't quite as useful as using generic types in a class, but it saves the overhead
and potential problems of down-casting and up-casting your types.

Summary

For a more detailed look at the C# language and the advanced wrinkles in generics, surf to the
C# Language Page on MSDN at http://msdn.microsoft.com/vcsharp/team/language. You'll
find a link that allows you to download a detailed white paper on the new C# language
specifications, along with useful insights from the C# language designers in the "Ask a
Language Designer" section of the site.

You might also like

pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy