- Dynamic Typed Objects
- Optional and Named Parameters
- Improved COM Interoperability
- Co- and Contra-Variance
Dynamic lookup
Dynamic lookup allows you to write method, operator and indexer
calls, property and field accesses, and even object invocations which
bypass the C# static type checking and instead gets resolved at runtime.
object calc = GetCalculator();
Type type = calc.GetType();
object result = type.InvokeMember(
"Add",
BindingFlags.InvokeMethod,
null,
new object[] { 10, 20 });
int sum = Convert.ToInt32(result);
With the C# 4.0 we would simply write the following code:
dynamic calc = GetCalculator();
int sum = calc.Add(10, 20);
In the above example we are declaring a variable, calc, whose static type is 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.
Named and optional parameters
Parameters in C# can now be specified as optional by providing a
default value for them in a member declaration. When the member is
invoked, optional arguments can be omitted. Furthermore, any argument
can be passed by parameter name instead of position.
public StreamReader OpenTextFile(
string path,
Encoding encoding = null,
bool detectEncoding = false,
int bufferSize = 1024) { }
COM specific interop features
Dynamic lookup as well as named and optional parameters both help
making programming against COM less painful than today. On top of that,
however, we are adding a number of other small features that further
improve the interop experience.
When you work with COM interop methods, you had to pass a reference to Missing.Value for unneeded parameters, like 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);
Now, in C# 4.0 you can only write:
doc.SaveAs(filename);
Variance
It used to be that an IEnumerable wasn't an IEnumerable
Generics with C# 4.0 now support safe co-variance and contra-variance through the use of the in (contra-variant) and out (co-variant) contextual keywords. Let's take a look at how this changes the definition of the IEnumerable and IEnumerator interfaces.
public interface IEnumerable<out T>
{
IEnumerator GetEnumerator();
}
public interface IEnumerator<out T>
{
T Current { get; }
bool MoveNext();
}
Given that an IEnumerable collection
is read only, there is no ability specified within the interface to
insert new elements, it is safe to treat a more derived element as
something less derived. With the out contextual keyword we are contractually affirming that IEnumerable is safely co-variant. We can now write the following code without a compiler error:
IEnumerable<string> strings = GetStrings();
IEnumerable<object> objects = strings;
Using the in contextual keyword we can achieve safe contra-variance, that is treating something less derived as something more derived.
public interface IComparer<in T>
{
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;
It is important to notice that co-variance in IEnumerable refers to the fact that its Current property can return a string instead of an object as output, while contra-variance in IComparer refers to the fact that its Compare method can accept an object instead of a string as input.