Tuesday, December 05, 2006 1:44 AM
bart
C# 3.0 Feature Focus - Part 3 - Collection Initializers
Introduction
In this C# 3.0 Feature Focus Week we'll focus on the new language features that will be part of C# 3.0 "Orcas". As you already might know, the major innovation on Orcas is the LINQ (Language Integrated Query) technology that will make data queries a first class citizen of the language (C# 3.0 and VB 9.0). In order to unlock this potential, a set of language features were added to the language that all work in concert to enable LINQ stuff. However, all of those individual features prove to be useful in isolation as well. Later, we'll focus on LINQ itself.
Collection Initializers
Everyone reading my blog should be familiar with this kind of code:
using System;
class Cini
{
public static void Main()
{
string[] s1 = new string[] { "Bart", "John", "Rob" };
string[] s2 = { "Bart", "John", "Rob" };
}
}
Quite convenient to initialize an array right away. However, did you ever try this?
using System;
using System.Collections.Generic;
class Cini
{
public static void Main()
{
List<string> l = new List<string>() { "Bart", "John", "Rob" };
}
}
In C# 2.0, this doesn't work. You can already guess what the outcome will be in C# 3.0, it works indeed! (Note: the short syntax without a new initialization instruction doesn't work however - CS0622). In reality, the code above is translated into:
using System;
using System.Collections.Generic;
class Cini
{
public static void Main()
{
List<string> l = new List<string>();
l.Add("Bart");
l.Add("John");
l.Add("Rob");
}
}
Verification in the IL code proves this:
.method public hidebysig static void Main() cil managed
{
.entrypoint
.locals init (class [mscorlib]System.Collections.Generic.List`1<string> V_0,
class [mscorlib]System.Collections.Generic.List`1<string> V_1)
IL_0000: nop
IL_0001: nop
IL_0002: newobj instance void class [mscorlib]System.Collections.Generic.List`1<string>::.ctor()
IL_0007: stloc.1
IL_0008: ldloc.1
IL_0009: ldstr "Bart"
IL_000e: callvirt instance void class [mscorlib]System.Collections.Generic.List`1<string>::Add(!0)
IL_0013: nop
IL_0014: ldloc.1
IL_0015: ldstr "John"
IL_001a: callvirt instance void class [mscorlib]System.Collections.Generic.List`1<string>::Add(!0)
IL_001f: nop
IL_0020: ldloc.1
IL_0021: ldstr "Rob"
IL_0026: callvirt instance void class [mscorlib]System.Collections.Generic.List`1<string>::Add(!0)
IL_002b: nop
IL_002c: ldloc.1
IL_002d: nop
IL_002e: stloc.0
IL_002f: ret
} // end of method Cini::Main
Behind the scenes, the compilers checks to see whether the type used implements IEnumerable and has a suitable Add method overload. So, doing the same with a non-generic collection like ArrayList won't work (CS1801: "The member initializer does not have an identifiable name") - update: this remark doesn't apply to the RTM release anymore:
using System;
using System.Collections;
class Cini
{
public static void Main()
{
ArrayList l = new ArrayList() { "Bart", "John", "Rob" };
}
}
An example to see this work against a self-made collection is shown below. For our convenience, we just wrap the List<T> collection type inside our own collection:
using System;
using System.Collections.Generic;
class Cini
{
public static void Main()
{
Bar<string> b = new Bar<string> { "Bart", "John", "Rob" };
}
}
public class Bar<T> : ICollection<T>
{
private List<T> lst = new List<T>();
public virtual void Add(T item)
{
Console.WriteLine(item);
lst.Add(item);
}
public virtual void Clear()
{
lst.Clear();
}
public virtual void CopyTo(T[] item, int n)
{
lst.CopyTo(item, n);
}
public virtual bool Contains(T item)
{
return lst.Contains(item);
}
public virtual int Count
{
get { return lst.Count; }
}
public virtual bool IsReadOnly
{
get { return ((ICollection<T>)lst).IsReadOnly; }
}
public virtual bool Remove(T item)
{
return lst.Remove(item);
}
public virtual IEnumerator<T> GetEnumerator()
{
return lst.GetEnumerator();
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return ((System.Collections.IEnumerable)lst).GetEnumerator();
}
}
Compiling and executing this code will print Bart, John and Rob on separate lines on the screen.
This feature becomes even more useful when combined with object initializers which were the subject of part 2 of this C# 3.0 Feature Focus series:
using System;
using System.Collections.Generic;
class Cini
{
public static void Main()
{
List<Customer> customers = new List<Customer>() {
new Customer { Name = "Bart", City = "Ghent", Age = 23 },
new Customer { Name = "John", City = "Zottegem", Age = 58 },
new Customer { Name = "Rob" , City = "Schoten", Age = 21 }
};
}
}
Try the same in C# 2.0 to see the difference with your own eyes. A quick calculation learns that we'd need 16 lines of code to achieve the same. Enjoy!
Del.icio.us |
Digg It |
Technorati |
Blinklist |
Furl |
reddit |
DotNetKicks
Filed under: C# 3.0