Menu

C# 2.0 Features

September 12, 2019 - C#, Programming

C# 1.0 was something of a first-pass as a language design. It received refinements and improvements, and started to create it’s own unique identity with 2.0. C# 2.0, like many C# versions, relied heavily on changes made to the CLR; that is, a C# 2.0 program typically could not run on the .NET 1.1 Framework runtime. This set it apart from Java, where language features are typically designed to run on any Java Virtual Machine.

Generics

One of the biggest features added to the language with C# 2.0 was Generics. Generics allow you to define type parameters which can “stand for” other classes, based on how you use the Generic class itself. Good examples of Generics can be found in their use within the .NET Base Class Library. With .NET 2.0 we got new strongly-typed classes such as List<T> and Dictionary<K,V>; These classes allow you to create strongly typed collections of any type and dictionaries using almost any type for the key and value types. This provides a wealth of flexibility to programming, as well as providing additional type safety by adding compile-time type checking. Their non-generic counterparts, ArrayList and Hashtable, would let you add anything. Even if your code expected all items in the ArrayList to be one type, you could introduce errors without realizing it by adding a String or number to the ArrayList, and it would still compile and run but you would receive run-time errors.

C#’s generics implementation is “first-class”; what this means is that the generic classes are preserved and are part of the compiled assembly. As a result, through reflection, it is possible to construct instances of a generic class with any type parameters. This is a result of the feature being implemented as part of the run-time. This is in contrast to how the same feature was implemented in Java. in Java, in order to allow the code that utilized Generics to run on older Virtual Machine implementations, Generics are implemented as part of the compilation. This results in something known as “Type erasure”- effectively, the generic class ceases to exist and java instead compiles the class as if the strongly-typed generic type definitions were the base Object type, after performing, of course, appropriate compile-time checks. In some instances the definition is replaced with the first bound class when type constraints are utilized. In either case, the downside to this implementation is that usage of reflection at run time is unable to construct generic type instances in the same manner as can be done in C#, resulting in much more complicated workarounds if that is desired.

Static Classes

C# 2.0 also introduced the idea of a static class. This is a class that cannot be instantiated and can only contain static members. Strictly speaking classes that function identically could be constructed by merely enforcing that a class would only contain such members, but with the static keyword being supported by class definitions this became a compile time check.

Nullable Types

Nullable types also got their start as early as C# 2.0. A nullable type allows you to effectively treat a value type as a reference type. This could allow you to indicate an extra data point (eg. a Nullable<int> field on a data class could accept null to indicate specific behaviour when being interpreted) This can be particularly useful when working with certain databases, as many fields may map to value types within .NET except for the fact that they can also be null within the database.

Anonymous Methods

Anonymous methods allowed C## 2.0 to define code methods that defined delegates within the body of other routines. This was in contrast to defining a separate routine and referencing it to construct the delegate. The big difference and advantage to an anonymous method is that it can eliminate the use of fields in that the anonymous method implementation can use the local variables where it is defined. There are a few caveats involved regarding what variables are closed over, but suffice it to say that there was some debate over whether anonymous methods actually allowed closures due to some of the specifics of handling local variables at the same scope as the anonymous method.

Partial Types

Partial types primarily benefit code generators, as they allow a single class definition to be split across multiple files, with each definition indicating it is a partial definition with the partial keyword. This primarily benefits code generators largely because if a class was large enough that it would be sensible to split into multiple files, then it would be more prudent to consider more extensive refactoring, such as creating new classes to handle some of the behaviour and code implementations.

Property Access Modifiers

With  C# 2.0, we got the ability to restrict accessibility with the get or set accessors of a property beyond the access level of the property itself. A property could be marked public, but a setter could be protected or even private. This allowed for the creation of properties that were for example read-only from the perspective of public clients, but with derived or internal routines could access. the way that was accomplished previously was by only defining the get accessor, and then making the backing field protected or private and accessing the backing field directly.

In addition to the additions to the language itself, since C# 2.0 paired with .NET Framework 2.0, it came with a bunch of new improvements, changes, and additions to the .NET Base Class Library. I won’t be covering those in depth here but aside from things like the introduction of things utilizing new CLR features such as generic collection classes, .NET 2.0 also added features to XMLDocument, Remoting, ASP.NET and ADO.NET.

Have something to say about this post? Comment!