The PDC is over and the big buzz seems to be around C# 3.0,
and Linq in particular. Linq relies on a number of new features in C# 3.0 such
as implicitly typed local variables, extension method, lambda functions and anonymous
types. A key enabler for the Linq project is the support for generics in the
CLR 2.0, and in theory you can achieve the same powerful features in C# 2.0,
but you would have to write much more code to do so. The expressive query
support in C# 3.0 is in a sense syntactic sugar, meaning that the compiler
generates code to support the new features.
The example below shows how you can declare an anonymous
type in C# 3.0
var p1=new
{Name="Arthur Dent",
FavoriteNumber=42 };
This is analogous with writing the following C# 1.0 code
public class AnonynousClass
{
private string name;
public string Name
{
get
{ return name; }
set
{ name = value; }
}
private int favoriteNumber;
public int FavoriteNumber
{
get
{ return favoriteNumber; }
set
{ favoriteNumber = value; }
}
}
...
AnonynousClass obj=new AnonynousClass();
obj.Name="Arthur Dent";
obj.FavoriteNumber=42;
Infact, the code created by the C# 3.0 compiler resembles
the code above. The only differences are the name of the anonymous type, that
the anonymous type is a generic class (so that it can be used within a
projection query) and that it overrides the method inherited from
System.Object. The most important difference is a non-technical one, namely the
amount of code needed to declare and use the type. Another thing to make note
of is that in C# 3.0 you can use the new var keyword to have the compiler infer
the type of a variable based on the expression used to initialize it.
Extension methods is the most important new feature for Linq
to be useful. Extension methods are static methods in a static class with the
new this modifier on the first parameter. When you import a class using the
using keyword, the extension methods become available on all types thru
instance method syntax. The example below show how you can declare a static
class with extension methods, and import this class to extend other types you
use.
public class Program
{
static void Main(string[] args)
{
var s="Hello World";
Console.WriteLine(s.WhoAmI());
}
}
public static class MyExtensions
{
public static string WhoAmI(this object obj)
{
return
obj.GetType().Name;
}
}
Many of the new features in C# 3.0 are well-known to
developers with experience from dynamic languages such as Python or Ruby. The
latter has support for mix-ins, which is a feature that resembles C# 3.0
extension methods. In Ruby you can define a mix-in on the module level. Modules
in Ruby cannot include instance methods because they are not classes, but you
can import modules within a class definition using the include statement. The
example below shows a Ruby implementation of the previous C# 3.0 snippet.
def
whoAmI?
"#{self.type.name}"
end
s =
"Hello World"
s.whoAmI?
As with C# 3.0’s var keyword, there is no need to explicitly
declare the variable s as a string. Ruby will infer the type based on what you assign
to it. A major difference between the two languages is that in C# type
inference is done at compile-time so you cannot assign another type, such as an
integer, to s after it has been declared. In Ruby type inference is done at
runtime. Therefore you can do the following in a Ruby application:
s =
"Hello World"
! Outputs “String”
s.whoAmI?
s=42
! Outputs “Fixnum”
s.whoAmI?
Extension methods is an elegant feature, and since you’re
able to import functionally into types, it pretty much eliminates the need for
multiple-inheritance.
Unfortunately, C# 3.0 won’t be generally available until Visual
Studio Orcas is released. Still you have some options to achieve the same
powerful feature today within both C# 1.0 and C# 2.0. As with all other new C#
3.0 features, extension methods is macro transformation that is carried out by
the compiler. The compiled code for the extension method example looks like
this:
[Extension]
public static class MyExtensions
{
[Extension]
public static string WhoAmI(object obj);
}
...
string s = "Hello World";
Console.WriteLine(MyExtensions.WhoAmI(text1));As you can see, a call is made to the static WhoAmI method
and the string s is passed as an argument. Delegating calls to a static method
adds a lot of complexity to your code, and is by far as elegant as the original
C# 3.0 code. If you’re not looking to extend any of the built-in types, such as
System.String or System.Int32, there is another way of mimicking extension
methods in both C# 1.0 and C# 2.0. You can wrap the type with a dynamic proxy.
The following example shows how you can use the Castle project's Dynamic Proxy
to wrap a Person class and mix-in a WhoAmI method.
class Program
{
static void Main(string[] args)
{
ProxyGenerator
generator=new ProxyGenerator();
GeneratorContext
context = new GeneratorContext();
WhoAmIMixin
whoAmIMixin = new WhoAmIMixin();
context.AddMixinInstance(whoAmIMixin);
Person
myPerson=(Person)
generator.CreateCustomClassProxy(typeof(Person),new WhoAmIInterceptor(),context);
myPerson.Name=”Arthur Dent”;
myPerson.FavoriteNumber=42;
Console.WriteLine(((IWhoAmI)myPerson).WhoAmI());
}
}
public class WhoAmIMixin : IWhoAmI
{
private Type actualType;
public Type ActualType
{
get
{ return actualType; }
set
{ actualType = value; }
}
public string WhoAmI()
{
return
actualType.Name;
}
}
public interface IWhoAmI
{
Type
ActualType
{
get;
set;
}
string
WhoAmI();
}
public class WhoAmIInterceptor
: StandardInterceptor
{
protected override void
PreProceed(IInvocation invocation, params object[] args)
{
IWhoAmI
whoAmIImpl=invocation.InvocationTarget as IWhoAmI;
if
(whoAmIImpl!=null)
{
whoAmIImpl.ActualType=invocation.Proxy.GetType().BaseType;
}
base.PreProceed(invocation,args);
}
}
public class Person
{
private string name;
public virtual string Name
{
get
{ return name; }
set
{ name = value; }
}
private int favoriteNumber;
public virtual int
FavoriteNumber
{
get
{ return favoriteNumber; }
set
{ favoriteNumber = value; }
}
}
The code is much more verbose than the C# 3.0 extension method
or the Ruby mix-in, but it does the same thing. The only difference to a
developer programming against your Person type is that she has to cast it to
the IWhoAmI interface to gain access to the WhoAmI method defined in it. While
it is more cumbersome to do this in the C# versions generally available today,
it is possible to extend objects with mix-ins. In a large solution there can be
much to gain by applying this technique, and now that the C# 3.0 cat is out of
the bag, you know what’s coming up and can align your design towards the new
and elegant features that are due in a year or two.