C# 3.0 Syntax Additions-Design Guidelines
C# 3.0 includes a few syntactical additions to the language. For the most part, Microsoft added these language additions to support Language Integrated Query (LINQ). These features include (but are not limited to) lambda expressions, extensions methods, anonymous types, implicitly typed local variables, automatic properties, and object initializers. | " | Extension methods have an innate ability to pollute your namespace with currently no way to scope them.
| " |
Most of the syntax additions fulfill very specific needs and should not reduce the importance of established coding and design methodologies and guidelines. When in doubt, prefer your established guidelines over the new syntax. Microsoft’s Anson Horton has a great article on LINQ’s impact on the design of C# 3.0, The Evolution Of LINQ And Its Impact On The Design Of C# that goes into detail of these language features from a different perspective. Lambda Expressions You can think of lambda expressions as an evolution of C# 2.0’s anonymous methods and it is an attempt at bringing functional programming-a paradigm that treats computation as the evaluation of mathematical functions and avoids state and mutable data-to C#. Lambda expressions are based upon lambda calculus. See the References sidebar for more information. PREFER methods over lambda expressions when the same code is used repeatedly. PREFER using lambda expressions where anonymous delegates would have been appropriate in C# 2.0. Extension Methods Arguably one of the most controversial additions to C#, extension methods allow designers to “inject” static methods into any other class. Essentially extension methods are syntactic sugar for creating a static method that operates on the instance of a particular type. Previous to C# 3.0, you might write a general utility method like this: public static bool IsStringPlural(String text, CultureInfo cultureInfo) {/* ... */}
…and may be called as such: String text = "languages"; Boolean isPlural = StringExtensions.IsStringPlural(text, new CultureInfo("en"));
…and operates on a String object (ideally telling you that that word/phrase is plural for that given context). Extension methods allow you to associate a similar method to a class in a way that allows it to be called as if it were a member of that class. Creating an IsPlural extension method for the String class doesn’t change much from the original syntax of the IsStringPlural method declaration-it just adds a this keyword in the parameter list: public static bool IsPlural(this String text, CultureInfo cultureInfo) {/* ... */}
…and may be called as such: String text = "languages"; Boolean isPlural = text.IsPlural( new CultureInfo("en"));
This method call syntax is arguably easier to read when used, but it reduces discoverability-there’s no way to tell from the line of code calling IsPlural that it really isn’t a member of String or what class the method really is declared in. Extension methods compile to ordinary static methods, and can be used as such: String text = "languages"; Boolean isPlural = StringExtensions.IsPlural(text, new CultureInfo("en"));
Extension methods are intended to be used in association with lambda expressions to provide vastly more readable and terser query expressions. The main drawback of extension methods is resolution. Essentially all extension methods are global; each method with the same name and same argument count cannot currently be differentiated. Extension methods have an innate ability to pollute your namespace with currently no way to scope. This means, by simply adding a using statement to your file you can introduce compile errors. DO use extension methods sparingly. DO put extension methods in their own static class. CONSIDER grouping extension methods that extend a particular class into a single static class and name that class “ClassNameExtensions”. If you do run into a name collision and are forced to use the static method call syntax, you don’t want to end up with reduced readability. DO keep extension method classes in their own namespace to mitigate potential name collisions (if you run into a name collision you’re forced back to using a static method call). Anonymous Types Anonymous types allow you instantiate a class without having to declare it. This, of course, requires that you use the var keyword because you don’t have a name for your class. For example: var person = new { Name = "Peter", Age=4};
As you might have inferred by the syntax, there’s no way to define a method on an anonymous type. This really restricts its usefulness in real-world scenarios. Since you’re not really declaring a type you can’t add attributes to the type; so, you can’t make it serializable. Another feature of anonymous types is that they are immutable. This essentially means when you instantiate an anonymous type you’re not declaring fields, but properties; and those properties only have a get, not a set. Anonymous types are really only useful for very short-lived data. AVOID anonymous types for long-lived data. |