In the past programming languages were developed to be either Object oriented or functional. But, today languages were being designed with several paradigms in mind including all best features of programming and functional capabilities.
The .net languages especially C# is no mere exception to this. The C# language has been witnessing many changes and enhancements in each version to enable the application developers of C# to utilize the real power of programming languages. Ever since, the outburst of the C# language, a .net language in 1998, with goal of creating a simple, modern, object oriented and type safe language, it has witnessed many enhancements in each release of the .net framework.
The 2.0 version of the language saw the evolution of the support for the following
1) Generics,
2) Anonymous methods,
3) Iterators,
4) Partial types and
5) Nullable types.
The 3.0 version predominantly concentrated on LINQ (Language Integrated Query) and as a side note to this LINQ, the additional features includes the following to facilitate the former:
1) Implictly Typed Local Variables.
2) Extension Methods.
3) Lambda Expressions.
4) Object and Collection Initializers.
5) Annonymous types.
6) Implicitly Typed Arrays.
7) Query Expressions and Expression Trees.
Coming to the upcoming version of C# which is 4.0 is more inspired by dynamic languages like Perl, Python and Ruby. There are both advantages and disadvantages in using both statical and dynamic languages. Some of the new features that we are going to see in the upcoming release of .net Framework in respect to C# are as described in following sections.
C# 4.0 language innovations include:
Dynamically Typed Objects.
Optional and Named Parameters.
Improved COM Interoperability.
Safe Co- and Contra-variance.
1. Let us consider this simple statically typed .NET class which calls the Add method on that class to get the sum of two integers:
Calculator calc = GetCalculator();
int sum = calc.Add(10, 20);
Our code gets all the more interesting if the Calculator class is not statically typed but rather is written in COM, Ruby, Python, or even JavaScript. Even if we knew that the Calculator class is a .NET object but we don't know specifically which type it is then we would have to use reflection to discover attributes about the type at runtime and then dynamically invoke the Add method.
object calc = GetCalculator();
Type type = calc.GetType();
object result = type.InvokeMember("Add",
BindingFlags.InvokeMethod, null,
new object[] { 10, 20 });
int sum = Convert.ToInt32(result);
If the Calculator class was written in JavaScript then our code would look somewhat like the following.
ScriptObect calc = GetCalculator();
object result = calc.InvokeMember("Add", 10, 20);
int sum = Convert.ToInt32(result);
With the C# 4.0 we would simply write the following code:
dynamic calc = GetCalculator();
int result = calc.Add(10, 20);
In the above example we are declaring a variable, calc, whose static type is dynamic. Yes, you read that correctly, we've statically typed our object to be dynamic. We'll then be using dynamic method invocation to call the Add method and then dynamic conversion to convert the result of the dynamic invocation to a statically typed integer.
You're still encouraged to use static typing wherever possible because of the benefits that statically typed languages afford us. Using C# 4.0 however, it should be less painful on those occassions when you have to interact with dynamically typed objects.
2. Another major benefit of using C# 4.0 is that the language now supports optional and named parameters and so we'll now take a look at how this feature will change the way you design and write your code.
One design pattern you'll often see as that a particular method is overloaded because the method needs to be called with a variable number of parameters.
Let's assume that we have the following OpenTextFile method along with three overloads of the method with different signatures. Overloads of the primary method then call the primary method passing default values in place of those parameters for which a value was not specified within the call to the overloaded method.
public StreamReader OpenTextFile(
string path,
Encoding encoding,
bool detectEncoding,
int bufferSize) { }
public StreamReader OpenTextFile(
string path,
Encoding encoding,
bool detectEncoding) { }
public StreamReader OpenTextFile(
string path,
Encoding encoding) { }
public StreamReader OpenTextFile(string path) { }
In C# 4.0 the primary method can be refactored to use optional parameters as the following example shows:
public StreamReader OpenTextFile(
string path,
Encoding encoding = null,
bool detectEncoding = false,
int bufferSize = 1024) { }
Given this declaration it is now possible to call the OpenTextFile method omitting one or more of the optional parameters.
OpenTextFile("foo.txt", Encoding.UTF8);
It is also possible to use the C# 4.0 support for named parameters and as such the OpenTextFile method can be called omitting one or more of the optional parameters while also specifying another parameter by name.
OpenTextFile("foo.txt", Encoding.UTF8, bufferSize: 4098);
Named arguments must be provided last although when provided they can be provided in any order.
3. If you have ever written any code that performs some degree of COM interoperability you have probably seen code such as the following.
object filename = "test.docx";
object missing = System.Reflection.Missing.Value;
doc.SaveAs(ref filename,
ref missing, ref missing, ref missing,
ref missing, ref missing, ref missing,
ref missing, ref missing, ref missing,
ref missing, ref missing, ref missing,
ref missing, ref missing, ref missing);
With optional and named parameters the C# 4.0 language provides significant improvements in COM interoperability and so the above code can now be refactored such that the call is merely:
doc.SaveAs("foo.txt");
When performing COM interoperability you'll notice that you are able to omit the ref modifer although the use of the ref modifier is still required when not performing COM interoperability.
With previous versions of the technologies it was necessary to also ship a Primary Interop Assembly (PIA) along with your managed application. This is not necessary when using C# 4.0 because the compiler will instead inject the interop types directly into the assemblies of your managed application and will only inject those types you're using and not all of the types found within the PIA.
4. The final language improvement that we will explore is co-variance and contra-variance and we'll begin by exploring co-variance with .NET arrays.
string[] names = new string[] {
"Anders Hejlsberg",
"Mads Torgersen",
"Scott Wiltamuth",
"Peter Golde" };
Write(names);
Since version 1.0 an array in the .NET Framework has been co-variant meaning that an array of strings, for example, can be passed to a method that expects an array of objects. As such the above array can be passed to the following Write method which expects an array of objects.
private void Write(object[] objects)
{
}
Unfortunately arrays in .NET are not safely co-variant as we can see in the following code. Assuming that the objects variable is an array of strings the following will succeed.
objects[0] = "Hello World";
Although if an attempt is made to assign an integer to the array of strings an ArrayTypeMismatchException is thrown.
objects[0] = 1024;
In both C# 2.0 and C# 3.0 generics are invariant and so a compiler error would result from the following code:
List
names = new List();
Write(names);
Where the Write method is defined as:
Generics with C# 4.0 now support safe co-variance and contra-variance through the use of the in and out contextual keywords. Let's take a look at how this changes the definition of the IEnumerable and IEnumerator interfaces.
public interface IEnumerable
{
IEnumerator GetEnumerator();
}
public interface IEnumerator
{
T Current { get; }
bool MoveNext();
}
You'll notice that the type parameter T of the IEnumerable interface has been prefixed with the out contextual keyword. Given that the IEnumerable interface is read only, there is no ability specified within the interface to insert new elements with the list, it is safe to treat something more derived as something less derived. With the out contextual keyword we are contractually affirming that IEnumerable is safely co-variant. Given that IEnumerable is safely co-variant we can now write the following code:
Because the IEnumerable interface uses the out contextual keyword the compiler can reason that the above assignment is safe.
Using the in contextual keyword we can achieve safe contra-variance, that is treating something less derived as something more derived.
public interface IComparer
{
int Compare(T x, T y);
}
Given that IComparer is safely contra-variant we can now write the following code:
IComparer[object] objectComparer = GetComparer();
IComparer[string] stringComparer = objectComparer;
Although the current CTP build of Visual Studio 2010 and the .NET Framework 4.0 has limited support for the variance improvements in C# 4.0 the forthcoming beta will allow you to use the new in and out contextual keywords in types such as IComparer. The .NET Framework team is updating the types within the framework to be safely co- and contra-variant.