There are a number of other interesting but fairly esoteric “features” of dynamic but one final one worth mentioning is accessibility.
Currently all methods and properties have to be public to be dynamically accessible. This isn’t a huge problem but it means that you can’t call private methods from within a class using dynamic parameters even though without the dynamic parameters the call would be perfectly legal.
Similarly you can’t use extension methods dynamically – the information to implement them isn’t available at run time. Anonymous functions can’t appear as arguments to a dynamic method call for the same reason. This makes it difficult to use LINQ queries over dynamic objects, which is strange given that LINQ was and is a major motivation for C# becoming more dynamic.
Beyond plain .NET objects
How the method invocation or property access is handled depends on the type of object that the dynamic type references.
You might think that the only possibility is the plain old .NET object but part of the reason for introducing dynamic is to make externally derived objects easier to work with.
In the case of a standard .NET object reflection is used to dispatch the operation. This is more sophisticated than you might imagine because any dynamic objects passed as parameters are resolved using reflection and then the resulting signature combined with reflection is used to make call to the appropriate method.
Moving beyond plain .NET objects a new class of dynamic objects can customise how they behave by implementing the IDynamicObject interface.
In this case the task of working out which method or property is needed is handed off to the object itself to work out using any method that suits. This is the key to building truly dynamic object models that can respond in sophisticated ways.
A very big advantage of dynamic types comes when you start to think about C#’s relationship with external and non-native objects – COM objects in particular. In this case a dynamic type is resolved using the COM IDispatch interface and this in turn means that you can use COM objects “raw”, i.e. without a Primary Interop Assembly (PIA).
As many COM objects make extensive use of the variant type, which can store any of a number of standard data types, being able to use dynamic types in place of variants is a big simplification.
For example, consider the standard difficulty encountered in using the Office COM object model:
The cast has to be included because the PIA uses object types to represent variants. Using dynamic types in place of objects makes it possible to dispense with the cast and allow the run time system to work out what is legal:
excel.Cells[1,1].Value= “some string”
Not using the PIA and driving the COM interface raw also means that you can hope to achieve a more efficient and lightweight program.
There are other minor enhancements to the way COM objects are dealt with in introduced in C# 4.0 that go together with dynamic types to make the whole thing easier to use.
For example, COM objects often pass parameters using pointers which result in the use of ref parameters in the corresponding C# methods. This can force you to create temporary variables to avoid any changes to a variable that you regard as logically being passed by value. Now the compiler will do the job for you by converting the value parameter to a temporary copy and passing this by reference.
The overall result is pass-by-value semantics for parameters that are passed as pointers.
Dynamic typing seems to be mostly harmless and it does improve the way that you can write some things that rely on determining type at runtime rather than compile time but it is a big change. It doesn't go as far as introducing a true dynamic approach to type but it is a change in philosophy.
Even if you regard it as an attempt to work with other systems that are less strict about type it is still a change that makes C# less pure and more ad-hoc.
Delegates are C#'s original way of allowing you to work with functions as if they were first class objects. The aim may be simple but the need to define a type and then an instance of the type can be [ ... ]
Passing parameters is easy as it always works in the same way, but the effects aren't always the same. It can be confusing and even error prone unless you understand how it all works. So does C# pass [ ... ]