To read the article online, visit http://www.4GuysFromRolla.com/articles/021809-1.aspx

An Extensive Examination of LINQ: Extension Methods, Implicitly Typed Variables, and Object Initializers

By Scott Mitchell


Introduction


One of the more substantive additions to the .NET Framework 3.5 and C# 3.0 and Visual Basic 9 languages was LINQ, a set of classes along with language enhancements that allow developers to use a common library and SQL-like query syntax to work with common data stores. The initial article in this series, An Introduction to LINQ, provided an overview of LINQ and its core pieces: the standard query operators, the language extensions that allow for LINQ's query syntax, and LINQ providers. We also looked at some simple LINQ examples using both the standard query operators and the query syntax.

LINQ's standard query operators - Select, Where, OrderBy, Average, and so on - can be used as if they were instance methods of any object that implements IEnumerable<T>. For example, given a string array named FileNames we can determine how many strings in the array start with the letter "S" by using the Where and Count standard query operators like so:

string[] FileNames = { ... };
int count = FileNames.Where(name => name.StartsWith("S")).Count();

The Where method and Count methods look like they are members of the Array class. However, they are defined in the Enumerable class in the System.Linq namespace as extension methods on the IEnumerable<T> interface. Also note the syntax that is used to specify the input parameter for the Where method: name => name.StartsWith("S"). This syntax is a lambda expression and provides a shorthand notation for developers to define a function.

This installment (and the next one) explore the language enhancements Microsoft made to C# 3.0 and Visual Basic 9 in more depth. (C# 3.0 and Visual Basic 9 are, at the time of this writing, the most recent versions of these two programming languages. They are the versions that were released with the .NET Framework 3.5 and Visual Basic 2008.) The key language enhancements that make LINQ possible include: extension methods; implicitly typed variables; object initializers; lambda expressions; and anonymous types. This article explores the first three of these language extensions; the latter two will be covered in the next installment. Read on to learn more!

Extension Methods


Every developer has, at one time or another, needed to incorporate some extra functionality in one of the .NET Framework base types. Consider string processing. While the String class in the .NET Framework includes a plethora of methods for parsing, trimming, and dissecting strings, there may some very specific string parsing logic needed for your application. Such custom functionality is commonly implemented by creating a "wrapper" class with methods that take in a string and return the parsed/processed string. Imagine that you needed a method that added a space after a letter if it was immediately followed by an uppercase letter, thereby transforming a string like "DisplayProduct" to "Display Product". The following method placed in a wrapper class would suffice.

public class StringHelpersCS
{
   public static string AddSpacesAfterCaseChange(string input)
   {
      if (string.IsNullOrEmpty(input))
         return input;

      StringBuilder output = new StringBuilder(input.Length * 2);

      // Add a space before each capitalized letter (except the first)
      for (int i = 0; i < input.Length; i++)
      {
         if (i > 0 && char.IsUpper(input[i]))
            output.Append(" ");

         output.Append(input[i]);
      }

      return output.ToString();
   }
}

(A Visual Basic version of this method is included in the code available for download at the end of this article.)

The above method starts by creating a StringBuilder named output. It then loops through each character in input and tacks it onto output. However, if the character under scrutiny is an uppercase character (and it's not the first character in the string), a space is added to output before the character. This method could be called from the application using the following syntax:

string pageFileNameLessExtension = "DisplayProduct";
string formattedPageTitle = StringHelpers.AddSpacesAfterCaseChange(pageFileNameLessExtension);

While wrapper classes offer a simple approach to encapsulate commonly used functionality, there is no tight association between the wrapper class's functionality and the type its methods operate on. In order to use the wrapper class, a developer must first know of its existence; he must dig into the various methods to see how they operate. Ideally, the AddSpacesAfterCaseChange method could be applied directly to a string as if it were one of the string members, using syntax like the following:

string pageFileNameLessExtension = "DisplayProduct";
string formattedPageTitle = pageFileNameLessExtension.AddSpacesAfterCaseChange();

This syntax makes it clear that the AddSpacesAfterCaseChange modifies a string. It also would allow for IntelliSense, causing the AddSpacesAfterCaseChange to show up in the IntelliSense drop-down after typing pageFileNameLessExtension and typing period.

The good news is that such functionality is possible in C# 3.0 and Visual Basic 9 via extension methods. In a nutshell, you can create a specially-formatted class and methods that are denoted to apply to a specific type (such as String). Once this method has been created, you can use it like it was an instance method on the type it extends, and Visual Studio includes the extension method in the IntelliSense drop-down. For example, to turn the AddSpacesAfterCaseChange method into an extension method on the String class we would only need to make two small changes. First, we would need to mark the class as static; second, we need to pass in the type the extension method operates on as the first parameter to the method using the this keyword.

public static class StringHelpersCS
{
   public static string AddSpacesAfterCaseChange(this string input)
   {
      ... Same code as before ...
   }
}

Now that the AddSpacesAfterCaseChange method is an extension method we can use it as if it was a method defined on the String class. We also get rich IntelliSense support in Visual Studio, as the following screen shot shows.

Extension methods appear in the Visual Studio IntelliSense drop-down.

Keep in mind that extension methods are syntactic sugar. At compile-time the extension methods that are invoked like instance methods are, in actuality, called using the wrapper class-style syntax - pageFileNameLessExtension.AddSpacesAfterCaseChange() is translated into StringHelpers.AddSpacesAfterCaseChange(pageFileNameLessExtension). Moreover, you don't have to use extension methods like an instance method. You can still invoke the method using the wrapper class syntax. For more background and instructions on creating extension methods, including examples in Visual Basic, be sure to read Extending Base Type Functionality with Extension Methods.

LINQ makes heavy use of extension methods. Recall that the myriad of standard query operators - Select, Where, OrderBy, Average, Count, and so on - work on objects that implement the IEnumerable<T> interface. These methods are defined in the Enumerable class in the System.Linq namespace as extension methods, thereby allowing them to be used like instance methods against any object that implements IEnumerable<T>.

Without extension methods, the LINQ standard query operator syntax would be less readable and wouldn't enjoy the IntelliSense support extension methods offer. To see the difference in readability, compare the following two code snippets, which are functionally identical. The first one uses the wrapper class syntax in calling the standard query operators.

// C#: Get the average of the odd Fibonacci numbers in the series...
int[] fibNum = { 1, 1, 2, 3, 5, 8, 13, 21, 34 };
double averageValue = Enumerable.Average(Enumerable.Where(fibNum, num => num % 2 == 1));


'VB: Get the average of the odd Fibonacci numbers in the series...
Dim fibNum() As Integer = {1, 1, 2, 3, 5, 8, 13, 21, 34}
Dim averageValue As Double = Enumerable.Average(Enumerable.Where(fibNum, Function(num) num Mod 2 = 1))

Whereas the second one uses the extension method syntax.

// C#: Get the average of the odd Fibonacci numbers in the series...
int[] fibNum = { 1, 1, 2, 3, 5, 8, 13, 21, 34 };
double averageValue = fibNum.Where(num => num % 2 == 1).Average();


'VB: Get the average of the odd Fibonacci numbers in the series...
Dim fibNum() As Integer = {1, 1, 2, 3, 5, 8, 13, 21, 34}
Dim averageValue As Double = fibNum.Where(Function(num) num Mod 2 = 1).Average()

The second snippet is more readable and is easier to see what operations are being performed and on what object as they are defined left to right (the fibNum array first has the Where operator applied, then the Average operator), whereas with the wrapper class syntax you have to work from the inside out.

Implicitly Typed Variables


All variables in .NET languages have an associated type. The type of a variable determines what values it can store and what properties and methods are available, and is specified when defining a variable. In the preceding example the fibNum variable's type is an array of integers, as was defined as such when declaring the variable (int[] fibNum and Dim fibNum() As Integer). With C# 3.0 and Visual Basic 9 you no long have to explicitly define a variable's type. Instead, you can use implicit typing. With implicit typing the variable still has a type, and that type still dictates what values the variable can store and the available properties and methods. However, with implicit typing the variable type is inferred dynamically based on the variable's initially assigned value.

To create an implicitly typed variable in C# use the var keyword; in VB, use the Dim keyword like usual but do not specify a type. The following snippet shows implicitly typed variables in use.

// C#
var displayCount = 5;
var message = "Hello, World!<br />";

for (int i = 0; i < displayCount; i++)
   Results.Text += message;


' VB
Dim displayCount = 5
Dim message = "Hello, World!<br />"

For i As Integer = 0 To displayCount - 1
   Results.Text &= message
Next

The types for variables message and displayCount are not defined; rather, their types are inferred by the values initially assigned to them. You can see that these variables have known types by hovering your mouse over the variable name. Visual Studio shows a tool tip that includes the variable's type.

While the type is not explicitly specified, the type is still known for implicitly typed variables.

What's important to keep in mind is that these implicitly typed variables are not loosely-typed. For example, you cannot change the type of the displayCount variable further down in the code by assigning it a different typed value. Rather, the type of the variable is fixed and determined by the initial value assigned to it. For this reason you must specify an initial value when using implicitly typed variables. That is, you cannot do the following:

// C# - This will not compile
var message;
message = "Hello, World!";


'VB - This will compile, but message will be of type Object
Dim message
message = "Hello, World!";

Implicitly typed variables are another example of syntactic sugar. At compile-time the implicitly typed code is turned into explicitly typed code. If implicitly typed variables seem a bit like a child's toy at this point - kind of neat and fun to play with, but not really all that useful in the real world - that's understandable. For simple types, implicitly typed variables do nothing more than save you a few keystrokes. Where implicitly typed variables are quite useful is when working with anonymous types. Anonymous types, as the name implies, are objects whose type is not explicitly defined. Consequently, you cannot create a variable of the type to hold the value of an object that is anonymously typed, and in that case implicitly-typed variables are a must. If this all sounds confusing, don't worry - we'll explore anonymous types in detail in the subsequent article in this series.

Object Initializers


A common pattern in object-oriented programming languages (like C# and Visual Basic) involves instantiating an object and immediately assigning values to a number of its properties. For example, given an Employee class with properties like EmployeeID, Name, Salary, and so forth, there might be several places where we have code like the following:

Employee emp = new Employee();
emp.EmployeeID = 1;
emp.Name = "Scott";
emp.Salary = 75000M;
...

This syntax is a bit verbose and lacks the initialization syntax that is available with simpler types. With integers, strings, Booleans, and the like we can specify the value of the variable when creating it:

int displayCount = 5;
string message = "Hello, World!";

Wouldn't it be nice to have the same flexibility with objects? The good news is that such functionality is now possible with C# 3.0 and Visual Basic 9. The following code shows how to create an Employee object in C# and specify values for some of its properties, all in the same statement.

Employee emp = new Employee
       {
         EmployeeID = 1,
         Name = "Scott",
         Salary = 75000M,
         ...
       }
;

The syntax for object initialization in Visual Basic is a little bit different. In VB you need to use the With keyword and prefix the property names with a period. Moreover, the With keyword and property assignments must be within the same statement, meaning they must all be on the same line of code or, if on different lines, separated by the statement continuation character, the underscore (_).

Dim emp As New Employee With { _
            .EmployeeID = 1, _
            .Name = "Scott", _
            .Salary = 75000, _
            ...
         }

The object initialization syntax allows for tighter and more readable syntax, but is especially useful when constructing anonymous types, a topic we'll look at in the subsequent installment.

Conclusion


The core functionality of LINQ is implemented in classes in the System.Linq namespace using syntax and technologies present in the .NET 2.0 Framework. However, LINQ's query syntax is possible due to a number of language enhancements made to C# 3.0 and Visual Basic 9. This article explored three of these language enhancements: extension methods, which allows for a method defined in one class to appear as an instance method in another class; implicitly typed variables, which are variables whose type is determined by the value they are assigned; and object initializers, which provide a shorthand notation for assigning property values for an instantiated object. There are two additional important language enhancements that we have yet to explore - lambda expressions and anonymous types. We'll examine these two language enhancements in the next article in this series.

Happy Programming!

  • By Scott Mitchell


    Attachments:


  • Download the code associated with this article series
  • Further Reading


  • New C# Language Features: Automatic Properties, Object Initializers, and Collection Initializers
  • Implicitly Typed Local Variables (C# Programming Guide)
  • New Language Feature: Extension Methods
  • Extending Base Type Functionality with Extension Methods
  • Article Information
    Article Title: ASP.NET.An Extensive Examination of LINQ: Extension Methods, Implicitly Typed Variables, and Object Initializers
    Article Author: Scott Mitchell
    Published Date: February 11, 2009
    Article URL: http://www.4GuysFromRolla.com/articles/021809-1.aspx


    Copyright 2014 QuinStreet Inc. All Rights Reserved.
    Legal Notices, Licensing, Permissions, Privacy Policy.
    Advertise | Newsletters | E-mail Offers