[.NET Languages 2005]

New Language Features

Sunday, September 12, 2004

The biggest feature are generics. C# offers other interesting novelties: anonymous methods, type inferrence, methods group conversions, iterators. The rest of the changes are minor compared to these, but are noteworthy.

Generics

Generics are the equivalent of C++ templates, although the feature set is different. This is a broad topic, and many tails will have to wait before being told.

Basics

Generics are similiar to C++ templates, but there are differences. C++ templates are textual; generics exist as part of the common type system of the CLR. A generic type can be exported from a managed DLL, and the type binding process (this is the equivalent of a template instantiation) uses just the meta data. Type binding can even occur at runtime (via reflection). Generics also support constraints, which increase type safety. On the downside, generics do only support type parameters as input. Partial and explicit specialization are not supported, either, and you can't use a type parameter as a base class, base interface, or interface implementation declaration.

Generics fall into two categories: generic types, and generic methods.

Generic Types

Generic types can be classes, structs, interfaces, and delegates, but not enums.

Here's an example of generic delegates:

delegate void Handler<T>(T arg);

class TestGenericDelegates {

    public static void Main(string[] args) {
        Handler<string> handler = new Handler<string>(HandleIt);
        handler("Hi Delegate!");
    }

    private static void HandleIt(string s){
        Console.WriteLine(s);
    }

}

A generic type may declare a base class or base interfaces. The base types may themselves be open constructed types, that is, a generic type along with type arguments defined in the (outer) generic type may be used:

class List<T> where T : IList<T> {}

Note that generic type definitions are not types themselves until constructed (bound, instantiated). That makes it possible to implement a generic interface multiple times, using different type arguments:

class C<T> : I<T> , I<string> {}

Here, I<T> and I<string> are different interface types, although they are based on the same generic type definition.

One useful approach is to derive classes from a generic type definition, and provide parameters for default cases (altough it leads to class forking), akin to typedefs:

class List<T> {}

class StringList : List<string> {}

A declared generic base type may be constructed with the type being declared as well. This is often the case when implementing generic interfaces:

struct Point : IComparable<Point> {

    public int CompareTo(Point pt) {}

}

Nested generic type definitions do not use the type parameters of their enclosing types; when the nested type is used, even within the scope of the outer type, all type parameters need to be specified. When you use a name for a nested type's type parameter that has already been used in the outer type, you get a warning:

class Hashtable<KeyType, ItemType> {

    private class Entry<KeyType, ItemType>{

        public Entry(KeyType key, ItemType value) {}

    }

    // CS0693: type parameter name already used in outer type
    public void Add(KeyType key, ItemType value){
        Entry<KeyType, ItemType> entry = new Entry<KeyType, ItemType>(key, value);
        // ...
    }
    
}

However, nested types can use (as opposed to redeclare) the type parameters of the outer types. This is especially useful in the case of nested delegate types:

class Wrapper<T> {

    delegate void WrapperCallback(T wrapped);

}

Any static (Shared) field of a generic type exists once per type instantiation (that is for every bound, or closed constructed type).

Likewise, static constructors are invoked once for every closed constructed type, subject to the usual conditions applying to static constructors.

Generic Methods

In a generic method, the generic parameter can be used for the return type, method parameters, or simply in the method implementation. The type of a generic argument can be inferred from a method argument if its type is the generic parameter, but not from the context in which the the return value is used:

T GetDefaultValue<T>(){
    return default(T);
}

void Print<T>(T value){
    Console.WriteLine(value);
}

void Test(){
    double d = GetDefaultValue<double>();
    Print(d);
}

Generic methods declared in generic types can use the generic parameters of the type (it is an error to declare generic type parameters on a generic method that are already declared by the enclosing type):

class List<T> {

    void Add(T value) {}
    
}

In VB.NET, generic methods cannot have local static variables. There is no technical or logical reason for this limitation; it just seems to be motivated by growing language complexity.

Implementing Generics

It is possible to obtain the default value of any generic type parameter:

Dim value As T = Nothing
T value = default(T);

The default value of a value type can always be created, since each value type must have a public default constructor. The default value of a reference type is null/Nothing, so it, too, is always available. If creating an instance of a reference type is required, a default constructor constraint (see below) can be used.

Constraining Generics

Constraints make generics actually usuable. It is information about the requirements that apply to the type arguments. There can be base class, interface, and default constructor constraints.

The basic practical consequences of constraints are casting and member access. Variables of unrelated type parameters can never be cast, but a constraint specifying a their relationship enables casting (see below for naked type constraints), and a base class or interface constraint makes the cast unnecessary. Member declared in a certain type (except System.Object) can only be invoked if there's a constraint for that type (unless a cast is used, but see below).

If a generic type parameter declares a base class constraint, members of the base class may be invoked on values or variables of that parameter type without casting. However, in the case of multiple base interface constraints, if several interfaces declare a member of the same name (and, if the member is overloadable, the same number alike-typed parameters), the call to an interface method needs to be disambiguated by a type cast:

class Base { public void Bar(){}}

interface I1 { void Foo(); }

interface I2 { void Foo(); }

class C <T> where T : Base, I1, I2 {

    public void Foo(T arg){
        arg.Bar();
        arg.Foo();       // ambiguous
        ((I2)arg).Foo(); // OK
    }

}

Note that it's still possible to cast a value/variable whose type is a generic parameter to some type that is not declared. But then, an InvalidCastException may occur at runtime.

A default constructor constraint ensures that instances of the generic type parameter may be created. It is not possible to constrain a type parameter to having a constructor with a given parameter list. In cases where this is desired (i.e., making sure that new objects can be created in initialized with certain values), using an interface constraint along with default constructor constraint achieves the same results:

// illegal: constructor constraint only for default constructors
// class FilterFactory <FilterType> where FilterType : new(string) {}

interface INameable {

    string Name{get;set;}

}

class FilterFactory <FilterType> where FilterType : INameable, new() {

    public FilterType CreateFilter(string sName){
        FilterType filter = new FilterType();
        filter.Name = sName;
        return filter;
    }

}

An interface forms a much stronger contract than parameterized constructor constraint ever could, because interface members have names, which say something about their semantics. What would a hypothetical constructor constraint of the form new(string) say about the meaning of the string parameter?

Constraints can be used as a way of requiring an object to support several types at once (as with interface constraints).

// equivalent to the illegal declaration:
// void RegisterServiceProvider(IServiceProvider, ICloneable provider);

void RegisterServiceProvider<T> (T provider) where T : IServiceProvider, ICloneable {
    // ...
}

Other useful constraints are such that constrain the type parameter to reference or value types, respectively:

where T : struct // T must be a value type
where T : class // T must be a reference type

You cannot use System.Enum as a type constraint in order to enforce enum type arguments (not System.ValueType, either). The problem is, the class System.Enum is a reference type, whereas enum types are value types. Type constraints must fall into either category, because the runtime implementation is quite different. This is a point where generics differ form C++ templates: in .NET generics, most operations on the argument type are checked in advance, without regard to type construction, whereas with templates, you may or may not get compiler errors depending on the type arguments.

What's needed here is a special kind of value type constraint (like where T : enum). A situation where the need to constrain a type argument to an enumerated type arises is in bit manipulation utility functions, such as those found in Gregor.Core.Bytes (GetBitFlag, etc.). What I want to say is this:

bool GetBitFlag<T>(T value, T mask){
    // error: can't apply operator
    return ((value & mask) == mask);
}

The closest workarround I've found is this:

bool GetBitFlag<T>(T value, T flag) where T : struct, IConvertible {
    ulong uval = value.ToUInt64(null);
    ulong uflag = flag.ToUInt64(null);
    return ((uval & uflag) == uflag);
}

That's quite a bit of runtime overhead for a little bit-testing helper. For additional type safety, there is the struct constraint; to be even more certain that the type is an enum, one could further constrain T to IComparable and IFormattable (which any enum happens to implement). It's not OK to party bitwise on any IConvertible.

Likewise, a type constraint can neither be a delegate type nor a delegate base type (System.Delegate, System.MulticastDelegate). I don't understand why the latter is the case (the former is because delegate types are final anyway).

A type constraint may also specify inheritance relationships between several type parameters. This is called a naked type contraint:

class Collection<T> {

    // E must be T or derived from T
    public void Add<E>(E item) where E : T {}

}

Note that a base type constraint allows using the constrain type itself as a type argument, not just derived types, as in the previous example.

Multiple type parameters can be constrained like this:

class C<T1, T2> where T1 : IComparable<T1> where T2 : IComparable<T2> {}

Overloading Generics

Two or more generic type definitions can overload each other, if the number of generic parameters is different. For that matter, a generic type definition can overload the definition of a non-generic type as well. It is not enough for a generic type to differ from another generic type only in its constraints:

class C {}

class C <T> {}

class C <T1, T2> {}

// error: C is already defined
class C <T> where T : IDisposable {}

Generic methods can be overloaded as well. Note that overloading by generic parameters can be used in addition to overloading by method parameters:

class TestGenericMethodOverloading {

    public static void Main(string[] args) {

        Foo<string>("Hello", "World");

    }

    private static void Foo<T>(string s){
        Console.WriteLine("Foo<T>(string)");
    }

    private static void Foo<T>(string s1, string s2){
        Console.WriteLine("Foo<T>(string, string)");
    }

    private static void Foo<T1, T2>(string s){
        Console.WriteLine("Foo<T1, T2>(string)");
    }

    private static void Foo<T1, T2>(string s1, string s2){
        Console.WriteLine("Foo<T1, T2>(string, string)");
    }

}

Note that you may also combine both generic parameter and method parameter overloading in a way that the generic argument type is deduced from the type of the method argument:

class TestGenericMethodOverloading {

    public static void Main(string[] args) {

        Bar("Hello", "World");

    }

    internal static void Bar<T>(T value){
        W(value);
    }

    internal static void Bar<T1, T2>(T1 value1, T2 value2){
        W(value1);
        W(value2);
    }

}

Generic Types and Reflection

First, a few definitions:

A generic type is the equivalent of a template - it's an unbound definition of a generic type (like class C<T>{}). A bound type, sometime called a "closed constructed type", is an "instantiation" of a generic type (like C<string>). A generic parameter is a placeholder used in a generic type definition (like T). An generic argument is a type that is substituted for a generic parameter when the type is bound (like string).

Here's an overview over the new members of the System.Type class that are related to generics:

Member Description
bool
IsGenericType
{get;}
Returns true for both generic type definition (class C<T>, Class C(Of T)), and types constructed from them, that is, bound types (C<string>, C(Of String)).
bool
IsGenericTypeDefinition
{get;}
Is true if the Type object represents the definition of an unbound generic type, like class C<T>{}. Note that you can obtain such a Type object with the typeof/GetType operators using an arity specifier (depending on the number of parameters): typeof(C<,>), GetType(C(Of ,)).
Type
GetGenericTypeDefinition();
Gets the the Type object representing the generic type definition (class C<T>{}) from a Type object that represents a bound type (that is, an instantiation of a generic type, such as C<string>;). You can get such a Type object like this: typeof(C<string>), GetType(C(Of String)).
Type
MakeGenericType
(params Type[] typeArgs);
Creates a bound type (in other words, instantiates a generic) from a Type object that stands for generic type definition. This is the counterpart of GetGenericTypeDefinition.
Type[]
GetGenericArguments();
Gets the generic parameters (for generic type definitions), or the generic arguments (for bound types). See above.
bool
ContainsGenericParameters
{get;}
True for the generic type definition, that is, IsGenericTypeDefinition is true as well.
bool
IsGenericParameter
{get;}
True if the Type object is used as a placeholder (generic parameter) in a generic type definition. False for Type object that stand for generic arguments (see GetGenericArguments above).
Type[]
GetGenericParameterConstraints();
If the Type object is a generic parameter, returns the constraints defined. The constraints are represented by an array of System.Type objects, each element corresponding to either a base class or an interface constraint. If the Type object does not represent a generic parameter, this method throws an exception.
GenericParameterAttributes
GenericParameterAttributes
{get;}
If the Type object stands for a generic parameter, returns an enum that further describes the constraints. This is where you find out about default constructor constraints, for example. If it's not a generic parameter, this property throws an exception.
int
GenericParameterPosition
{get;}
If the Type object is a generic parameter, returns its position. Otherwise, throws an exception.

For a generic type, the IsGenericTypeDefinition property returns true; for a bound type, it returns false, but IsGenericType returns true. For types that are neither generic type definitions nor bound types, IsGenericType is false.

The following example illustrates how generic types are reflected:

using System;
using System.Collections;
using System.Collections.Generic;

public class TestGenericsReflection {

    public static void Main(string[] args) {
        try{
            Run();
        }catch(Exception ex){
            W(ex);
        }
    }

    private static void Run(){

        Type tpNormal = typeof(string);
        ShowType(tpNormal, "Normal");

        W(string.Empty);

        Type tpBound = typeof(C<string, int>);
        ShowType(tpBound, "Bound");

        W(string.Empty);

        Type tpGeneric = tpBound.GetGenericTypeDefinition();
        ShowType(tpGeneric, "Generic");

        W(string.Empty);

        Type[] types = {typeof(string), typeof(DateTime)};
        Type tpDynBound = tpGeneric.BindGenericParameters(types);
        ShowType(tpDynBound, "Dynamically bound");

    }

    private static void ShowType(Type tp, string sHint){
        W(sHint + " type: " + tp);
        W("- is generic type:             " + tp.IsGenericType);
        W("- is generic type definition:  " + tp.IsGenericTypeDefinition);
        W("- contains generic parameters: " + tp.ContainsGenericParameters);
        foreach(Type tpArg in tp.GetGenericArguments()){
            W("  Argument type: " + tpArg);
            W("  - is generic parameter:      " + tpArg.IsGenericParameter);
            if(tpArg.IsGenericParameter){
                W("  - generic parameter attribs: " + tpArg.GenericParameterAttributes);
                foreach(Type tpConstraint in tpArg.GetGenericParameterConstraints()){
                    W("    Constraint type: " + tpConstraint);
                }
            }
        }
    }

    internal static void W(object obj){
        Console.WriteLine(obj);
    }

}

class C<T1, T2> where T1 : IComparable, IEnumerable
                where T2 : new() {

    public void Foo(T1 t1, T2 t2){
        Console.WriteLine(t1 + "; " + t2);
    }

}

The output:

Normal type: System.String
- is generic type:             False
- is generic type definition:  False
- contains generic parameters: False

Bound type: C`1[System.String,System.Int32]
- is generic type:             True
- is generic type definition:  False
- contains generic parameters: False
  Argument type: System.String
  - is generic parameter:      False
  Argument type: System.Int32
  - is generic parameter:      False

Generic type: C`1[T1,T2]
- is generic type:             True
- is generic type definition:  True
- contains generic parameters: True
  Argument type: T1
  - is generic parameter:      True
  - generic parameter attribs: NonVariant
    Constraint type: System.IComparable
    Constraint type: System.Collections.IEnumerable
  Argument type: T2
  - is generic parameter:      True
  - generic parameter attribs: NoSpecialConstraint, DefaultConstructorConstraint
    Constraint type: System.Object

Dynamically bound type: C`1[System.String,System.DateTime]
- is generic type:             True
- is generic type definition:  False
- contains generic parameters: False
  Argument type: System.String
  - is generic parameter:      False
  Argument type: System.DateTime
  - is generic parameter:      False

Generic Methods and Reflection

Once you're familiar with generic types reflection API, reflecting on generic methods is easy. Here is the list of the relevant methods.

bool IsGenericMethod{get;}
bool IsGenericMethodDefinition{get;}
MethodInfo GetGenericMethodDefinition();
MethodInfo MakeGenericMethod(params Type[] typeArgs);
Type[] GetGenericArguments();
public override bool ContainsGenericParameters{get;}

On the type array returned by GetGenericArguments, you can call the methods listed above that deal with generic parameters.

Generics in the Framework

Primarily, the BCL uses generics for collections. These types are found in System.Collections.Generic. There are generic equivalents for the usual interfaces (IEnumerable, ICollection, IList, IEnumerator, and so on), as well as classes, most notably, List<T>. Related to collections, there is the IComparable<T> interface in the System namespace.

Other Generic Support in the Languages

When using foreach on a generic collection, the loop variable's type must be compatible with (that is, assignable or implicitly convertible from) the collection's element type (applies to both C# and VB.NET). This means that no casts are emitted by the compiler, and no InvalidCastException can be thrown in an foreach statement anymore. Note that this is not the case with user-defined strongly-typed collections, which aren't foreach-safe.

Anonymous Methods (C# only)

Anonymous methods can be used like Java's anonymous inner classes (which are ad-hoc interface implementations or class extensions). In C#, anonymous methods work closely with delegates; the provide an ad-hoc implementation for a method that can be called back by a delegate:

TextBox txt = new TextBox();

txt.Click += delegate(object sender, EventArgs e){
                 Console.WriteLine(sender + ": " + e);
             };

txt.Click += delegate{
                 Console.WriteLine("TextBox.Click");
             };

Note that the instantiation of the delegate has a special syntax: it's not a "new <delegate-type>" expression; rather, the type is inferred from the context.

Also note that you need not specify a formal parameter list for the anonymous method if it does not use any of the arguments.

The "context" from which the delegate type is deduced can be the left side of an assignment, the return type of a method, or the type of a method parameter. Here's an example of how the type of the delegate is inferred from the return type of a method:

class C {

    delegate void Handler(string s);

    public static void Main(string[] args) {

        Handler h = GetHandler();
        h("Hello");
    }

    private static Handler GetHandler(){
        return delegate(string s){
                   Console.WriteLine(s);
               };
    }

}

Resolving ambiguities

As noted, you can use an anonymous method anywhere a delegate object is expected, even as arguments in a method call. If the method is overloaded by having different delegate types as parameters in the same position, the delegate type cannot be inferred from the context. Rather, you need to declare the delegate parameter list in the anonymous method expression, so that the compiler can disambiguate:

delegate void Handler1(object o);

delegate void Handler2(string s);

class C {

    public static void Main(string[] args) {

        // overload resolution fails for overloaded method DoIt
        DoIt(delegate{Console.WriteLine(string.Empty);});

        // OK, call goes to DoIt(Handler2)
        DoIt(delegate(string s){Console.WriteLine(s);});

    }
    
    private static void DoIt(Handler1 handler){
        handler(new object());
    }
    
    private static void DoIt(Handler2 handler){
        handler("A string.");
    }

}

This is a bit like VB.NET's rules for using "AddressOf <method>" vs. "New <delegate-type>(AddressOf <method>)"

Equality of anonymous methods

For each anonymous method, the compiler creates a (specially named) method. If you register an anonymous method as an event handler, and want to remove it later on, be sure to keep a reference to the delegate object. That is because semantically identical anonymous method expressions are permitted, but not required to be equal. This means that an event handler removal expression using an anonymous method will result in the creation of another anonymous method, which will likely not be consired equal to a previous one (as with the current implementation):

class C {

    private static event EventHandler Click;

    public static void Main(string[] args) {
                 
        Click += delegate{
                     Console.WriteLine("Handler 1");
                 };
        ClickIt();

        // creates another anon method rather than
        // removing the previous handler:
        Click -= delegate{
                     Console.WriteLine("Handler 1");
                 };
        ClickIt();
        
        EventHandler handler = delegate{
                                   Console.WriteLine("Handler 2");
                               };
        Click += handler;
        ClickIt();

        // because the delegate object is identical to the one used
        before, it's necessarily equal, thus the handler is removed:
        Click -= handler;
        ClickIt();
        
    }
    
    private static void ClickIt(){
        if(Click != null){
            Click(null, EventArgs.Empty);
        }
    }

}

Besides, even if semantically identical anonymous method expressions were guaranteed to be equal, who would even want to duplicate the code in anonymous methods for the purpose of dynamic event handler management? In these scenarios, anonymous methods are not what you'd use.

Procedure nesting?

Anonymous methods maybe used like nested procedures. The operative word here is "like", because they do not provide the optimizations usually performed for nested procedures. There is still a method call, and that even goes through a function pointer. However, if the goal is to encapsulate some code that's used more than once in a method within that method itself, that's doable with anonymous methods. The following routine traces nodes in a node list, first all of them, then only the leaf nodes; for each node traced, there is a string representing all of its attributes; this string is cached in a hashtable; the string building and caching code is encapsulated in an anonymous method:

delegate string AttributeStringGetter(Node node);

void TraceTheNodes(NodeList nodes){

    Hashtable<Node, string> nodeAttributes = new Hashtable<Node, string>();

    AttributeStringGetter getter = delegate(Node node){
        if(false == nodeAttributes.ContainsKey(node)){
            StringBuilder sb = new StringBuilder();
            foreach(Attribute attr in node.Attributes){
                sb.Append(attr.ToString());
                sb.Append(" ");
            }
            string s = sb.ToString();
            nodeAttributes.Add(node, s);
            return s;
        }else{
            return nodeAttributes[node];
        }
    };

    Console.WriteLine("All nodes:");
    foreach(Node node in nodes){
        Console.WriteLine(getter(node));
    }

    Console.WriteLine("Leaf nodes:");
    foreach(Node node in nodes){
        if(node.IsLeaf){
            Console.WriteLine(getter(node));
        }
    }
}

Note that if there was only one loop, the anonymous method would be pointless (code readability aside); from a strict source-code-normalization point of view, like an ordinary function, it only makes sense because its code is called from two distinct places in the outer method (as opposed to simply being called several times from one code point, as in a loop).

Closures

Anonymous methods "capture" any local variables and parameters in the method scope that includes the anonymous method. This is known as a closure.

One situation where closures are useful is when using callback-style iterators. When designing iteration code, there are basicly two ways to do it: write the iteration code in a procedural fashion, often using recursion, and let the client define a callback function; or, provide an iterator object, keeping track of state in member variables, and let the client do everything locally. The first option is easier for designer of the collection/iterator, while the second one allows the client to keep state more easily. Closures allow using the first approach, but the client can use all local variables in the anonymous method:

// natural way to iterate through a tree
void WalkTree(Node root, Callback cb){
    cb(root);
    foreach(Node kid in root.Children){
        WalkTree(kid, cb);
    }
}

// easy way to use iteration code, and keeps things local
void Test(){
    StreamWriter writer = new StreamWriter(@"C:\Contents.txt");
    Node root = new FileSystemTree(@"C:\");
    WalkTree(root,
             delegate(Node node){
                 writer.WriteLine(node);
             });
    writer.Close();
}

However, C# provides yet another way to address the iterator problem (see below for Iterators as a language feature).

An important aspect of closures in C# is that the "L-value" of the enclosing locals is captured, not just the "R-value". This means that the anonymous method can manipulate the outer variables.

On a related note, since anonymous methods are members of the enclosing type, you can use the "this" reference to refer to other members of that type. There's no need for an "enclosing instance" like in Java anonymous classes, and the resulting implicitness and disintuitive semantics of "this".

Use and abuse

The downside of using anonymous methods is that while they can simplify the code in certain cases, they tend to make real-world code much harder to read. For example, if you create a couple of controls in a form's constructor, and attach handlers to a couple of events of each control, you end up with a Javaesque mess of dozens of code lines and several levels of indentation. For this, this feature will not change my event handling style, except for simple "try-out" programs.

Type Inferrence and Method Group Conversions (C#)

Some of this has already been discussed above, in connection with anonymous methods. We need to distinguish two things that the C# compiler possibly does when it compiles an assignment to a delegate context (such as a delegate variable or an event):

  1. Determining which delegate type to instantiate.
  2. Selecting the right method out of a method group (overload candidate set). In this regard, possibly selecting a method whose signature does not exactly match the delegate type, but is consistent with it.

Note: as for type inference, I'm only discussing it in connection with delegate creation in this section.

Type Inference

The first thing is known as type inference. The context (for example, the formal type of the left side of the assignment) determines which delegate type must be instantiated.

Note that the context can be ambigious (in the case of passing a delegate to a parameter of an overloaded method, see above).

Type inference - in the case of delegate assignment - is required if there is no explicit delegate creation ("new <delegate-type>") expression; there are two cases of this:

// anonymous method
Handler h1 = delegate{Console.WriteLine("Handler")};

// regular method
Handler h2 = HandleIt;

If the inferred delegate type has a return type that is not void, the anonymous method must return a value - note that the compiler's error message isn't "not all code paths return a value", but rather "cannot convert from method group to <delegate type>".

Method Group Conversions

First, let me mention something that isn't new, but important to keep in mind because of what will follow. Once the type of the delegate to create is known (either through an explicit "new" expression or through type inferrence), and a regular method is named as the delegate target, the right overload is chosen:

delegate void Handler(string s);

void HandleIt(){}

void HandleIt(string s){}

void Test(){

    // HandleIt(string) is the target
    Handler h = new Handler(HandleIt);

}

What's new is that when type inferrence occurs, method group conversions may apply. Such conversions affect the method selection described above. If a candidate method does not exactly match the delegate signature, but its parameter types are "wider" and its return type "narrower", and the parameter types are neither "ref" nor "out", and all types are reference types (this is the simplest case), a method group conversion succeeds.

Here are the two cases of method group conversions that go hand in hand with type inferrence:

delegate object Getter(string sName);

object GetIt(int i){return "It";}

string GetIt(object obj){return "It";}

void Test(){

    // GetIt(object) is consistent with Getter
    Getter g1 = GetIt;

    // this anonymous method is consistent with Getter as well
    Getter g2 = delegate{return "It";};

}

Permitting a handler method to have a return type derived from the delegate return type is known as covariance. Permitting a handler method parameter to have a type that's a base type of the respective delegate parameter type is called contravariance.

A third case where method group conversions can occur is the explicit creation of a new delegate:

delegate object Getter<T>(T key);

void Test(){

    // exact match
    Getter<object> g1 = delegate(object obj){return "It";};

    // method group conversion
    Getter<string> g2 = new Getter<string>(g1);

}

Note that the mere assignment of a delegate reference of one type to a delegate reference of another type does not afford method group conversions (neither implicitly nor implicitly). The new delegate object created has the same invocation list as the original.

Delegate Relaxation

Note that method group conversions are a compile-time process. However, the dynamic delegate creation facilities of the .NET framework (System.Delegate.CreateDelegate) now also support the runtime equivalent of C# method group conversions - called delegate relaxation. So that is now functionally equivalent to what Gregor.NET's delegate creation API's (Gregor.Core.Reflect.CreateDelegate) do:

using System;
using System.Reflection;

class FireEventArgs : EventArgs {}

delegate void FireEventHandler(object sender, FireEventArgs e);

static class Program {

    public static void Main(string[] args) {

        // static, private, relaxed
        FireEventHandler h1 =  (FireEventHandler) Delegate.CreateDelegate(
                    typeof(FireEventHandler), typeof(Program), "HandleIt");
        h1(null, new FireEventArgs());

        // instance, private, relaxed
        EventSink es = new EventSink();
        MethodInfo mi = es.GetType().GetMethod(
              "HandleIt", BindingFlags.Instance | BindingFlags.NonPublic);
        FireEventHandler h2 = (FireEventHandler) Delegate.CreateDelegate(
                                        typeof(FireEventHandler), es, mi);
        h2(null, new FireEventArgs());
    }

    private static void HandleIt(object sender, FireEventArgs e){
        Console.WriteLine("HandleIt: " + e);
    }

}

class EventSink {

    private void HandleIt(object sender, EventArgs e){
        Console.WriteLine("HandleIt: " + e);
    }

}

Note that no method group conversions are performed in Delegate.Combine, as well as its compile-time equivalent, C#'s += operator. For this, look at the CombineDelegate<T>() function in Gregor.Core.DelegateUtil - you pass the new delegate type explicitly as a generic type parameter, and it returns combined new instances of that type (for each entry in each given delegate's invocation list). When creating the new instances, delegate signature checking is relaxed.

See below for notes on how System.Delegate.CreateDelegate() works, to get an idea of how simple it was to implement DelegateUtil.CombineDelegate().

Open Instance and Closed Static Delegates

Open instance delegates are delegates pointing to instance methods having an explicit "this" parameter, but they're not bound to a specific target object until invocation time.

Closed static delegates are the counterpart of open instance delegates. They point to a static method that has at least one parameter; that parameter is bound to the delegate at creation time; when the delegate is invoked, no argument is supplied for that first parameter.

Both open instance and closed static delegate are created with the static CreateDelegate() method of the System.Delegate class, using the following overloads (in other words, those that have a second parameter called "firstArgument"):

Delegate CreateDelegate(Type type, object firstArgument, MethodInfo method);
Delegate CreateDelegate(Type type, object firstArgument, MethodInfo method, bool throwOnBindFailure);

These functions are perfect for creating regular (that is, non-bound static and instance-bound delegates) delegates as well. It compares the delegate signature against the method signature:

Knowing these rules, it's easy to use these two overloads of CreateDelegate. They are the most flexible and most thought-out ones available. What's more, when creating delegates based on existing ones, one can simply pass the "Target" and "Method" properties for any kind of delegate, the things will just work. Delegate relaxation, aka method group conversions, are also performed.

Both open instance and closed static delegate types have signatures which differ from the target method, but correspond to the arguments supplied in the call. An open instance delegate has an additional parameter for the target type at position zero, whereas a closed static delegate comes without the first parameter of the target method:

delegate void OpenInstanceDelegate(object target);
delegate void ClosedStaticDelegate();

class Sample {

    public        void Foo()          {}
    public static void Bar(object obj){}

}

class Test {

    public static void Main(string[] args){

        MethodInfo mi1 = typeof(Sample).GetMethod("Foo");
        Delegate del1 = Delegate.CreateDelegate(
                            typeof(OpenInstanceDelegate),
                            null, // "this"
                            mi1);
        OpenInstanceDelegate oid = (OpenInstanceDelegate) del1;
        oid(new Sample());

        MethodInfo mi2 = typeof(Sample).GetMethod("Bar", BindingFlags.Static | BindingFlags.Public);
        Delegate del2 = Delegate.CreateDelegate(
                            typeof(ClosedStaticDelegate),
                            "Hello", // first argument
                            mi2);
        ClosedStaticDelegate csd = (ClosedStaticDelegate) del2;
        csd();
    }

}

I have convenience methods in Gregor.Core.DelegateUtil for creating open instance method and property delegates, as well as closed static delegates. As for the methods that create open instance delegates, the target type is taken from the type of the first parameter of the delegate type generic argument supplied:

public static DelegateType CreateOpenInstanceDelegate<DelegateType>(
    string sMethodName
);

public static DelegateType CreateOpenInstancePropertyGetter<DelegateType>(
    string sPropertyName
);

public static DelegateType CreateOpenInstancePropertySetter<DelegateType>(
    string sPropertyName
);

public static DelegateType CreateClosedStaticDelegate<DelegateType, TargetType>(
    string sMethodName,
    object firstArgument
);

Iterators (C# only)

C# iterators are one of the coolest new language features. They allow you to define the implementation of an iterator using nothing more than a simple block of code. The advantage is that you can save all state as local variables (at least at the source-code level). Behind the scenes, the compiler emits an iterator class (with a very special name), which captures all the local variables and arguments, and keeps track of the state.

The syntax

An iterator is defined as a method that returns IEnumerator, IEnumerable, or their generic equivalents, and that uses the (context) keyword "yield" at least once:

public IEnumerator GetEnumerator(){
    yield return "Hello";
    if(DateTime.Now.Month == 13){
        yield break;
    }
    yield return "World";
}

Implementation

The following paragraphs describe how iterators work, but only in a more or less simplified way.

When a client calls GetEnumerator, an instance of a class implementing IEnumerator is created. When the client calls MoveNext, the first "yield return" statement is executed, and "Hello" becomes the new value of the Current property. Control is transferred back to the client immediately. When a "yield break" statement or the end of the block is encountered, the next call to MoveNext returns false.

If an iterator method returns IEnumerable or IEnumerable<T>, an object implementing said interfaces is created. However, the enumerable object is not populated with values immediately; rather, this is delayed until a client calls IEnumerable.GetEnumerator (or the generic equivalent); then, an enumerator object is created (actually, C# optimizes that somewhat, reusing the same enumerable object for the first enumerator that's created), which iterates through the yield statement step by step. The advantage here is that collection transformer methods (like filters, slicers) need not populate the new collection immediately; if a client breaks the foreach loop, that safes time and memory:

public IEnumerable Slice(int from, int length){
    for(int i = from; i < from + length; i++){
        yield return m_Values[i];
    }
}        

Uses and Limitations

Note that an anonymous method can not be an iterator. However, an anonymous method may come in handy as support for the iterator, for example, in a filter-like iterator method returning IEnumerable:

delegate bool FilterCallback(object obj);

static class CollectionUtil {

    public static IEnumerable Filter(IEnumerable col, FilterCallback callback){
        foreach(object obj in col){
            if(callback(obj)){
                yield return obj;
            }
        }
    }
    
}

class C {

    public static void Main(string[] args) {

        string[] aStr = {"Hello", "_Hi", "World", "_Universe"};

        foreach(string s in CollectionUtil.Filter(
            aStr,
            delegate(object obj){
                return ((string)obj).StartsWith("_");
            }
        )){
            Console.WriteLine(s);
        }

    }

}

It's tricky to split the implementation of an iterator over several methods. If there is even one yield statement, can cannot use regular return statements (you can end the iteration with yield break):

using System;
using System.Collections;
class Test {

    public static void Main(string[] args) {
        IEnumerator walker = GetWalker();
        while(walker.MoveNext()){
            Console.WriteLine(walker.Current);
        }
    }

    private static IEnumerator GetWalker(){
        yield return "Hello";
        // CS1622: return GetWalker2();
        GetWalker2(); // forget it
    }

    private static IEnumerator GetWalker2(){
        yield return "World";
    }
}

// output:
// Hello

The second function gets called, but it yields no output. The solution is to actually iterate over the other iterator:

using System;
using System.Collections;
class Test {

    public static void Main(string[] args) {
        IEnumerator walker = GetWalker();
        while(walker.MoveNext()){
            Console.WriteLine(walker.Current);
        }
    }

    private static IEnumerator GetWalker(){
        yield return "Hello";
        foreach(object obj in GetWalker2()){
            yield return obj;
        }
    }

    private static IEnumerable GetWalker2(){
        yield return "World";
    }
}

// output:
// Hello
// World

Contrast that against calling (wrapper) functions that call iterators - that of course works, you can pass an iterator-defined enumerator or enumerable object arround.

Nullable Types

Microsoft is back on the way to Limbo Value Hell. The System namespace offers the Nullable generic struct, which makes any type nullable.

System.Nullable is intended for value types only. If you bind it to a reference type, you get a compiler warning. The reason is gratuitious keyword reuse: you can assign "null" (or "Nothing") to any Nullable value:

Nullable<string> s = null; // CS0461
Nullable<int> i = null;
Console.WriteLine(s.HasValue); // false
Console.WriteLine(i.HasValue); // false

Of course, a null reference continues to be a different thing.

C# goes a step further, introducing a special syntax to declare nullable values, along with special null-coalescing operator.

Partial Types

Partial types allow a class, interface or struct definition to be spread over several files. This is useful for code generation. The syntax is obvious:

public partial class C {

    public void Foo(){}

}

public partial class C {

    // error
    public void Foo(){}

}

What's interesting is that not all type modifiers need to be consistent over serveral parts of the type. While the visibility modifiers need to agree, other modifiers (like abstract, sealed, or static) need not be present on each part. I think adding a bit syntactic salt would go a long way towards maintainable code in this regard.

Likewise, custom attributes need not be applied to all parts of the type. In this case, the more permissive approach is OK with me, because custom attributes can occur in a great number, so the expressiveness of the code needs to be balanced against increased redundancies.

In VB.NET partial classes, it's enough to mark just one part as partial. Although all parts of a partial types must be defined in one project, it's still a gratuitious loss of encapsulation and maintainability (just think of working in a version-controlled environment, and someone else wants to hack the class that you've checked out). By making it "easy", and trying not to confuse the "user", VB.NET is well on its way to become as full of leaky abstractions as Classic VB was.

Modules, aka, Static Classes (C#)

C# introduces a module feature, which is deemed a "design-pattern" rather that an "outdated feature that is not really OO". C# modules work the same way as VB.NET modules (except for VB.NET's automatic function import within the same project, and the implicit Sharedness of module members).

CLR-wise a module will always be a "type", and any language can call it's "static methods". A module implies certain restrictions (like no inheritability, no use in variable declarations, etc.). Neither VB.NET nor C# fully recognize each other's modules for what they are: while instantiation and inheritance won't work (constructors are private; the "type" is sealed), it's OK to declare variables.

In Reflection, a C# module is a type for which both IsAbstract and IsSealed are true; a VB.NET module is considered sealed, but not abstract; so we need to look for a custom attribute (Microsoft.VisualBasic.CompilerServices.StandardModuleAttribute). Modules in the Gregor.NET framework are - until now - recognizable by the presence of a Gregor.Core.ModuleAttribute.

Custom Events (VB.NET)

Like in C#, you can implement custom event management in VB.NET 2005:

Module EventUtil

    Public Sub AddEventHandler(ByVal map As IDictionary(Of String, System.Delegate), _
                               ByVal sName As String, _
                               ByVal newValue As System.Delegate)
        If map.ContainsKey(sName) Then
            Dim handler As System.Delegate = map(sName)
            map(sName) = System.Delegate.Combine(handler, newValue)
        Else
            map.Add(sName, newValue)
        End If
    End Sub

    Public Sub RemoveEventHandler(ByVal map As IDictionary(Of String, System.Delegate), _
                                  ByVal sName As String, _
                                  ByVal newValue As System.Delegate)
        If map.ContainsKey(sName) Then
            Dim handler As System.Delegate = map(sName)
            map(sName) = System.Delegate.Remove(handler, newValue)
        End If
    End Sub

End Module
    
Class Sender
    
    Private Const EVENT_FIRE As String = "Fire"

    Public Custom Event Fire As EventHandler
        RaiseEvent(ByVal sender As Object, ByVal e As EventArgs)
            If m_Events.ContainsKey(EVENT_FIRE) Then
                Dim handler As EventHandler = DirectCast(m_Events(EVENT_FIRE), EventHandler)
                handler(sender, e)
            End If
        End RaiseEvent
        AddHandler(ByVal newValue As EventHandler)
            EventUtil.AddEventHandler(m_Events, EVENT_FIRE, newValue)
        End AddHandler
        RemoveHandler(ByVal newValue As EventHandler)
            EventUtil.RemoveEventHandler(m_Events, EVENT_FIRE, newValue)
        End RemoveHandler
    End Event
    
    Private m_Events As Dictionary(Of String, System.Delegate)

    Public Sub New()
        MyBase.New()
        m_Events = New Dictionary(Of String, System.Delegate)()
    End Sub
    
    Protected Overridable Sub OnFire(ByVal e As EventArgs)
        RaiseEvent Fire(Me, e)
    End Sub
    
    Public Sub Foo()
        Me.OnFire(EventArgs.Empty)
    End Sub

End Class

Custom Conversions and Operators (VB.NET)

VB.NET 2005 offers custom conversions and operators much like C#. Since I'm not a fan of code implicitly executing all over the place, I'm somewhat reluctant to describe the details. Let's just say that custom operators in managed languages have stronger confines than in C++, although there are still a number of things one can mess up which the compiler does not detect, namely implementation consistancy of the Equals method with equality operators, implementation consistancy of the CompareTo method with comparison operators, and implemenation consistency of the "definite true"/"definite false" operators with any boolean conversions.

VB.NET has an edge over C# in this largely irrelevant battle: custom equality/inequality operators are invoked through the "=" and "<>" operators, respectively, whereas identity comparison (reference equality) is tested with the "Is" and "IsNot" operators.

The "My" Namespace (VB.NET)

The My namespace, besides putting an end to death and taxes, is Microsoft's answer to the question: "Why can't the stupid computer just do what I want?"

Besides the friendly, post-modern consumer-style term "My", the most problematic thing with this feature is that the compiler implicitly creates assembly-specific properties that return things like forms in the application that one can access through modules in the "My" namespace.

Otherwise, it is supposed to be a "speed dial" into the (really rather well-organized and thus already easily approachable) .NET class library. The VB.NET class library offers wrap-ups of certain things (like networking, file system, etc.) that any application can use. Like I said, I think these wrappers are mostly pointless, because either the framework is already simple enough to use, or otherwise it's the developers task to wrap things up: if a framework cannot make it any easier, then any further simplification should be project-specific. In other words, MS should either put these things into the proper namespaces in the .NET class library (if they think they're useful), or leave it to the developer. Offering these things for a specific language makes no sense. Of couse, someone may reply that it's possible to reference the VB.NET runtime library from a C# project as well, but that sort of makes my point, doesn't it?

Miscellaneous

Explicit Zero-based Arrays (VB.NET)

Since the array declaration syntax in VB.NET 2002 through 2003 was pretty wretched, the old explicit bounds syntax is back, although you can't use a lower bound other than zero (unless you use System.Array.CreateInstance).

Dim eternals(0 To 41) As Boolean

Mixed Visibility Properties

These are a feature of classic VB that I had already forgotten:

Public Property Name() As String
    Get
        Return m_Name
    End Get
    Friend Set(ByVal newValue As String)
        m_Name = newValue
    End Set
End Property 

C# has it, too:

public string Name{
    get
        return m_Name;
    }
    internal set{
        m_Name = value;
    }
}

Reference Inequality Operator (VB.NET)

Finally, VB.NET has an "IsNot" operator.

Dynamic Cast Operator (VB.NET)

C++ has a "dynamic_cast" operator that returns a NULL pointer if the cast fails; managad C++ (at least version 7) has a "__try_cast" operator that throws a managed exception if the cast fails. VB.NET calls its dynamic cast operator "TryCast". Why the VB.NET team cannot seem to follow terminological precendents set elsewhere (even if it's in a different language) is beyond my understanding. C# uses the "as" operator for dynamic casting, by the way.

TryCast must have a reference type as the target operand, because it requires a way of signalling failure safely (as by yielding Nothing). Contrast this with DirectCast, for which the source operarand must be a reference, because for a value type, everything is known up front (although the VB 8 Beta seems to have relaxed this rule).

Continue Statement (VB.NET)

There's now a continue statement just like in C-based languages. It's a bit more flexible, however, in that you can specify the type of loop to be continued:

Do While f
    For Each s As String In strings
        If s Is Nothing Then
            Continue Do
        End If
    Next s
Loop

Using Blocks (VB.NET)

They work much like the equivalent C# feature.

Fixed inline buffers (C#)

In structs, the new "fixed" keyword allows to define an array of a fixed length allocated within the struct itself. This is useful for calling native code, and it's simpler than using custom attributes ("SizeConst"), and remembering to still dimension the array.

Conditionally-applied Custom Attributes

The ConditionalAttribute, which so far was only applicable to methods (subject to certain requirements), can now be applied to custom attribute classes. The result is an attribute that is only applied when a given compile time constant is set:

[Conditional("DEBUG")]
class ExperimentalAttribute : System.Attribute {}

Friend Assemblies

With the new InternalsVisibleTo custom attribute, you can expose Friend or internal types and members to code in specific assemblies.