features are part of LINQ. They are not; they are part of the .NET Framework 3.5 and the VB 9 and C# 3.0 languages. They are very valuable in their own rights as well as playing a huge role for LINQ.
This article will demonstrate and discuss several key language features including:
- Automatic Property setters/getters
- Object Initializers
- Collection Initializers
- Extension Methods
- Implicitly Typed Variable
- Anonymous Type
Automatic Properties
Since creating classes by hand can be monotonous at times, developers use either code generation programs and IDE Add-Ins to assist in creating classes and their properties. Creating properties can be a very redundant process, especially when there is no logic in the getters and setters other than getting and setting the value of the private field. Using public fields would reduce the code required, however public fields do have some drawbacks as they are not supported by some other features such as inherent data binding.
public class Customer { private int _customerID; private string _companyName; private Address _businessAddress; private string _phone; public int CustomerID { get { return _customerID; } set { _customerID = value; } } public string CompanyName { get { return _companyName; } set { _companyName = value; } } public Address BusinessAddress { get { return _businessAddress; } set { _businessAddress = value; } } public string Phone { get { return _phone; } set { _phone = value; } } }
how the same result can be achieved through automatic properties with less code than
public class Customer { public int CustomerID { get; set; } public string CompanyName { get; set; } public Address BusinessAddress { get; set; } public string Phone { get; set; } }
Object Initializers
It is often helpful to have a constructor that accepts the key information that can be used to initialize an object. Many code refactoring tools help create constructors like this with .NET 2. However another new feature coming with .NET 3.5, C# 3 and VB 9 is object initialization. Object Initializers allow you to pass in named values for each of the public properties that will then be used to initialize the object.
For example, initializing an instance of the Customer class could be accomplished using the following code:
Customer customer = new Customer(); customer.CustomerID = 101; customer.CompanyName = "Foo Company"; customer.BusinessAddress = new Address(); customer.Phone = "555-555-1212";
However, by taking advantage of Object Initializers an instance of the Customer class can be created using the following syntax:
Customer customer = new Customer { CustomerID = 101, CompanyName = "Foo Company", BusinessAddress = new Address(), Phone = "555-555-1212" };
The syntax is to wrap the named parameters and their values with curly braces. Object Initializers allow you to pass in any named public property to the constructor of the class. This is a great feature as it removes the need to create multiple overloaded constructors using different parameter lists to achieve the same goal. While you can currently create your own constructors, Object initializers are nice because you do not have to create multiple overloaded constructors to handle the various combinations of how you might want to initialize the object. To make matters easier, when typing the named parameters the intellisense feature of the IDE will display a list of the named parameters for you. You do not have to pass all of the parameters in and in fact, you can even use a nested object initialize for the BusinessAddress parameter, as shown below.
Customer customer = new Customer { CustomerID = 101, CompanyName = "Foo Company", BusinessAddress = new Address { City="Somewhere", State="FL" }, Phone = "555-555-1212" };
Collection Initializers
Initializing collections have always been a bother to me. I never enjoy having to create the collection first and then add the items one by one to the collection in separate statements. (What can I say, I like tidy code.) Like Object Initializers, the new Collection Initializers allow you to create a collection and initialize it with a series of objects in a single statement. The following statement demonstrates how the syntax is very similar to that of the Object Initializers. Initializing a List
ListcustList = new List { customer1, customer2, customer3 };
Collection Initializers can also be combined with Object Initializers. The result is a slick piece of code that initializes both the objects and the collection in a single statement.
ListcustList = new List { new Customer {ID = 101, CompanyName = "Foo Company"}, new Customer {ID = 102, CompanyName = "Goo Company"}, new Customer {ID = 103, CompanyName = "Hoo Company"} };
The List
Customer customerFoo = new Customer(); customerFoo.ID = 101; customerFoo.CompanyName = "Foo Company"; Customer customerGoo = new Customer(); customerGoo.ID = 102; customerGoo.CompanyName = "Goo Company"; Customer customerHoo = new Customer(); customerHoo.ID = 103; customerHoo.CompanyName = "Hoo Company"; ListcustomerList3 = new List (); customerList3.Add(customerFoo); customerList3.Add(customerGoo); customerList3.Add(customerHoo);
Extension Methods
Have you ever looked through the list of intellisense for an object hoping to find a method that handles your specific need only to find that it did not exist? One way you can handle this is to use a new feature called Extension Methods. Extension methods are a new feature that allows you to enhance an existing class by adding a new method to it without modifying the actual code for the class. This is especially useful when using LINQ because several extension methods are available in writing LINQ query expressions.
For example, imagine that you want to cube a number. You might have the length of one side of a cube and you want to know its volume. Since all the sides are the same length, it would be nice to simply have a method that calculates the cube of an integer. You might start by looking at the System.Int32 class to see if it exposes a Cube method, only to find that it does not. One solution for this is to create an extension method for the int class that calculates the Cube of an integer. Extension Methods must be created in a static class and the Extension Method itself must be defined as static. The syntax is pretty straightforward and familiar, except for the this keyword that is passed as the first parameter to the Extension Method. Notice in the code below that I create a static method named Cube that accepts a single parameter. In a static method, preceding the first parameter with the this keyword creates an extension method that applies to the type of that parameter. So in this case, I added an Extension Method called Cube to the int type.
public static class MyExtensions { public static int Cube(this int someNumber) { return someNumber ^ 3; } }
When you create an Extension Method, the method sows up in the intellisense in the IDE, as well. With this new code I can calculate the cube of an integer using the following code sample:
int oneSide = 3; int theCube = oneSide.Cube(); // Returns 27
As nice as this feature is I do not recommend creating Extension Methods on classes if instead you can create a method for the class yourself. For example, if you wanted to create a method to operate on a Customer class to calculate their credit limit, best practices would be to add this method to the Customer class itself. Creating an Extension method in this case would violate the encapsulation principle by placing the code for the Customer’s credit limit calculation outside of the Customer class.
However, Extension Methods are very useful when you cannot add a method to the class itself, as in the case of creating a Cube method on the int class. Just because you can use a tool, does not mean you should use a tool.
Anonymous Types and Implicitly Typed Variables
When using LINQ to write query expressions, you might want to return information from several classes. It is very likely that you'd only want to return a small set of properties from these classes. However, when you retrieve information from different class sources in this manner, you cannot retrieve a generic list of your class type because you are not retrieving a specific class type. This is where Anonymous Types step in and make things easier because Anonymous Types allow you to create a class structure on the fly.
var dog = new { Breed = "Cocker Spaniel", Coat = "black", FerocityLevel = 1 };
Notice that the code above creates a new instance of a class that describes a dog. The dog variable will now represent the instance of the class and it will expose the Breed, Coat and Ferocity properties. Using this code I was able to create a structure for my data without having to create a Dog class explicitly. While I would rarely create a class using this feature to represent a Dog, this feature does come in handy when used with LINQ.
When you create an Anonymous Type you need to declare a variable to refer to the object. Since you do not know what type you will be getting (since it is a new and anonymous type), you can declare the variable with the var keyword. This technique is called using an Implicitly Typed Variable. When writing a LINQ query expression, you may return various pieces of information. You could return all of these data bits and create an Anonymous Type to store them. For example, let’s assume you have a List
ListcustomerList = new List { new Customer {ID = 101, CompanyName = "Foo Co", BusinessAddress = new Address {State="FL"}}, new Customer {ID = 102, CompanyName = "Goo Co", BusinessAddress = new Address {State="NY"}}, new Customer {ID = 103, CompanyName = "Hoo Co", BusinessAddress = new Address {State="NY"}}, new Customer {ID = 104, CompanyName = "Koo Co", BusinessAddress = new Address {State="NY"}} }; var query = from c in customerList where c.BusinessAddress.State.Equals("FL") select new { Name = c.CompanyName, c.BusinessAddress.State }; foreach (var co in query) Console.WriteLine(co.Name + " - " + co.State);
Pay particular attention to the select clause in the LINQ query expression. The select clause is creating an instance of an Anonymous Type that will have a Name and a State property. These values come from 2 different objects, the Customer and the Address. Also notice that the properties can be explicitly renamed (CompanyName is renamed to Name) or they can implicitly take on the name as happens with the State property. Anonymous Types are very useful when retrieving data with LINQ.
Summary
There are a lot of new language features coming with .NET 3.5 that both add new functionality and make the using of existing technologies easier. As we have seen in the past, when new technologies have been introduced, such as with generics, they often are the precursors to other technologies. The introduction of Generics allowed us to create strongly typed lists. Now because of those strongly typed lists of objects we will be able to write LINQ query expressions against the strongly typed objects and access their properties explicitly even using intellisense. These new features such as Object Initializers and Anonymous Types are the building blocks of LINQ and other future .NET technologies.
No comments:
Post a Comment