An Extensive Examination of LINQ: Extension Methods, Implicitly Typed Variables, and Object InitializersBy Scott Mitchell
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 -
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
Count standard query operators like so:
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
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!
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
Stringclass 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.
(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
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
before the character. This method could be called from the application using the following syntax:
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.
AddSpacesAfterCaseChange method could be applied directly to a string as if it were one of the string members, using syntax like the following:
This syntax makes it clear that the
AddSpacesAfterCaseChange modifies a string. It also would allow for IntelliSense, causing the
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
we need to pass in the type the extension method operates on as the first parameter to the method using the
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.
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
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 -
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
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.
Whereas the second one uses the extension method syntax.
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
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
fibNumvariable's type is an array of integers, as was defined as such when declaring the variable (
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.
The types for variables
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.
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
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:
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.
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
Employeeclass with properties like
Salary, and so forth, there might be several places where we have code like the following:
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:
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.
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.
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 (
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.
The core functionality of LINQ is implemented in classes in the
System.Linqnamespace 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.