In order to keep the footprint small, not every possible C# feature is supported by .NET Micro Framework. However, some require only small trick to make them working (no, generics are not the case). I thought it might come handy to write it all down at one place.
First, a table.
People seem to like tables. —Raymond
The table contains mix of different features. Some are only syntax sugar, some are deep CLR changes and some require framework libraries support. The point is that all of them require the C# compiler to do some additional stuff or support additional keywords.
If you find out a way how to get any of the red items working, I will update the table – let me know.
Those who remember Visual Studio .NET might know that quite lot of things came with CLR 2.0 and C# 2.0. The most important and complex change was, sure, the introduction of generics, but there were also other important things, now in daily use. Fortunately, most of them work out of the box:
Purpose: To be able to split one class, struct or interface into several files. Details
Example:
Causes: The compiler to join the files in single class/struct/interface.
Status: Works without troubles.
Purpose: Class which cannot be instantiated or inherited. Contains only static members and has no instance constructors. Details
Causes: The compiler to not include a default instance constructor.
Purpose: To differentiate between types with same name, and to reference the root namespace. Details
Causes: Helps compiler to resolve namespaces.
Status: Works without troubles, but the extern alias requires special build parameters to work, so it is not standardly available.
Purpose: To allow properties different access for read and write. Details
Causes: The compiler to apply modifiers to the generated set_ and get_ methods.
Purpose: To allow inline methods without names. Details
Causes: The compiler to generate an instance method with a name of its choice.
Purpose: No need to instantiate the delegate.
Causes: The compiler to find out (infer) the required delegate type and convert it to the code above.
Purpose: Provide a degree of flexibility when matching method signatures with delegate types. Details
Causes: The compiler to accept valid delegates.
Purpose: Allow to reference variables out of the anonymous method's scope.
Causes: The compiler to generate nested class for storing the variable. This can get you into troubles, consider the following code:
for (int i = 0; i < 10; i++) { Dispatcher.BeginInvoke(new System.Threading.ThreadStart(delegate { System.Diagnostics.Debug.Print("i = " + i); })); } Now, read it again: The compiler generates a class to store the variable. So the output will be 10, 10, 10, 10, 10, 10, 10, 10, 10, 10. To get the desired behaviour, you must convince the compiler that the variable is not going to change ever, for example: for (int i = 0; i < 10; i++) { int iFixed = i; Dispatcher.BeginInvoke(new System.Threading.ThreadStart(delegate { System.Diagnostics.Debug.Print("i = " + iFixed); })); } Now it is as expected: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9.
Purpose: To simplify enumerable types implementation. Details
Causes: The compiler to generate a nested class implementing a state machine.
You might also want to check a very nice article in MSDN Magazine Create Elegant Code With Anonymous Methods, Iterators, And Partial Classes for more details on the above features, their implementations and how to effectively use them.
Purpose: Type safety, code reuse and improved performance. Details
Causes: In contrast to all features above, this is a fundamental CLR feature. The type substition occurs at runtime, not compile time. Not only because that, generics are not C++ templates.
Status: Currently not available in .NET Micro Framework due to the significant footprint demands.
Purpose: Allow value types to have no value, be uninitialized. Details
Causes: Substituted by generic types System.Nullable<T>.
Status: Currently not available in .NET Micro Framework, because it requires generics.
Purpose: Simplify code. Details
Causes: a ?? b is substituted by a != null ? a : (b).
The flagship of C# 3.0 was LINQ (Language-Integrated Query) and almost all of the new language features were introduced to make it possible, or look good. C# 3.0 uses the CLR 2.0, so no changes for .NET Micro Framework.
Causes: The compiler to create the backing field automatically with a name of its choice.
Purpose: Allow creation of anonymous types, simplify code. Details
Causes: The compiler to insert a code which instantiates the required type, sets desired fields or properties, and assigns it to the declared variable.
Causes: The compiler to prepend a code which instantiates the required type, calls its Add methods, and assigns it to the declared variable.
Status: Works without troubles. The object being initialized is of course required to have a public Add method accepting single parameter, public parameterless constructor, and, not sure why, inherit the System.Collections.IEnumerable interface, even unimplemented.
Purpose: Allow storage of anonymous types, simplify code writing. Details
Causes: The compiler to substitute the correct type. At compile time. Not runtime.
Causes: The compiler to insert the correct type.
Purpose: Add instance functionality to a type you cannot control, in a syntax easy way, allows query expressions. Details
Causes: The compiler to replace the extension calls with respective static calls.
Status: Works, but the compiler needs to mark the extension methods and their class using System.Runtime.CompilerServices.ExtensionAttribute, which is not present in the standard .NET Micro Framework libraries. It is easy to supply one, though:
using System; namespace System.Runtime.CompilerServices { [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Class | AttributeTargets.Method)] public sealed class ExtensionAttribute : Attribute { } }
Purpose: Easy syntax for short anonymous methods, allows selections and conditions in query expressions. Details
Causes: Treated as standard anonymous method.
Status: Works, but you need to declare the delegates. System.Func<> types are not available due to lack of generics.
Purpose: Store method evaluation tree, allows queries to be optimized. Details
Causes: The compiler to insert plenty of code constructing the tree using types in System.Linq.Expressions namespace.
Status: Not available due to lack of generics (and all the tree classes). You could declare the non-generic classes yourself, but you won't be able to tell the compiler to use them.
Purpose: Integrate natural queries into language. Details
Causes: The compiler to convert the query into extension methods and lambda functions, resp. static method calls and anonymous functions.
Status: Works, but you need to declare all the extension methods. See Mobile Software Engineering blog for an example of implementation.
Purpose: Encapsulate set of read-only properties without the need to declare a type, used in queries selection. Details
Causes: The compiler to generate the class for you, properties are read-only (underlaying fields readonly).
Status: Not available, unfortunately the current C# requires generics in the generated class (and a System.Text.StringBuilder).
Purpose: Allow generated code to be regenerated without affecting developer's code, used in LINQ to SQL Designer. Details
Causes: The compiler to merge the methods if an implementation exists, otherwise completely remove it including all calls to it.
And for the fourth version, the flagship was the Dynamic Language Runtime I guess, which is of course not present in .NET Micro Framework. In the world of .NET Framework, it is needed to support some dynamic languages, e.g. IronPython, and to simplify the COM interopability. Neither of this is needed on .NET Micro Framework.
Purpose: Bypass static type checking and resolve object members at runtime. Details
Causes: Nested site container class is generated and calls to a binder are used to manipulate the object.
Status: The Dynamic Language Runtime is not available on .NET Micro Framework (and the generated code uses generics).
Purpose: Eliminate the need to remember or lookup parameters order. Details
Causes: The compiler to match the parameters and reorder them correctly.
Purpose: Specify default values of method parameters to be able to omit them during calls. Details
Causes: The C# compiler substitutes missing parameters with their defaut values. At compile time. This means that if the default value is changed, callers are not updated until recompilation.
Status: Works without troubles. The System.Runtime.InteropServices.OptionalAttribute and DefaultParameterValueAttribute need not to be specified, this is a CLR feature.
Purpose: Allow implicit conversion of generic types. Details
Causes: The compiler to allow and runtime to consider implicit conversions of type parameters when working with generics.
Status: Generics are not available.