Language Enhancements

Several .NET language enhancements were introduced with .NET 3.5. These language enhancements, along with features such as generics, are important pieces to building queries with LINQ.

This section will demonstrate and discuss several key language features, including:

  • Automatic property setters/getters

  • Object initializers

  • Collection initializers

  • Extension methods

  • Implicitly typed variables

  • Anonymous types

Figure 1-3 shows the .NET 3.5 language enhancements.

.NET 3.5 language enhancements at a glance

Figure 1-3. .NET 3.5 language enhancements at a glance

Automatic Properties in C#

Creating properties can be a very redundant process, especially when the getters and setters contain no logic other than getting and setting the value of the private field. Using public fields would reduce the amount of code; however, public fields do have some drawbacks. Most importantly, public fields are not fully supported by data binding.

One way to avoid having to type the code for a private field and its public property getter and setter is to use a refactoring tool. However, automatic properties allow you to type less code and still get a private field and its public getter and setter. You create an automatic property using shortened syntax. The compiler will generate the private field and the public setter and getter for you. In Example 1-1, a Customer2 class and an Address2 class have several private fields that are exposed through a series of corresponding public property getters and setters.

Note

C# supports automatic properties, but VB does not.

Example 1-1. Explicit getters and setters in C#

    public class Customer2
    {
        private int id;
        private string companyName;
        private Address2 companyAddress;

        public int ID
        {
            get { return id; }
            set { id = value; }
        }

        public string CompanyName
        {
            get { return companyName; }
            set { companyName = value; }
        }

        public Address2 CompanyAddress
        {
            get { return companyAddress; }
            set { companyAddress = value; }
        }
    }

    public class Address2
    {
        private int id;
        private string city;
        private string state;

        public int ID
        {
            get { return id; }
            set { id = value; }
        }

        public string City
        {
            get { return city; }
            set { city = value; }
        }

        public string State
        {
            get { return state; }
            set { state = value; }
        }
    }

Example 1-2 shows how you can achieve the same result through automatic properties with less code than Example 1-1. The Customer and Address classes use automatic properties to create the class properties without requiring all of the code to declare a field and its property getter and setter.

Example 1-2. Automatic properties

    public class Customer
    {
        public int ID { get; set; }
        public string CompanyName { get; set; }
        public Address CompanyAddress { get; set; }
    }

    public class Address
    {
        public int ID { get; set; }
        public string City { get; set; }
        public string State { get; set; }
    }

Object Initializers

Object initializers allow you to pass in named values for each public property that will then be used to initialize the object. For example, you could initialize an instance of the Customer class using the code shown in Example 1-3.

Example 1-3. Creating and initializing a class

C#
Customer customer = new Customer();
customer.ID = 1001;
customer.CompanyName = "Foo";
customer.CompanyAddress = new Address();
VB
Dim customer As New Customer()
customer.ID = 1001
customer.CompanyName = "Foo"
customer.CompanyAddress = New Address()

However, by taking advantage of object initializers, you can create an instance of the Customer class using the syntax in Example 1-4.

Example 1-4. Using object initializers

C#
Customer customer = new Customer
{
    ID = 1001,
    CompanyName = "Foo",
    CompanyAddress = new Address()
};
VB
Dim customer = New Customer() With _
    {.Id = 1001, .CompanyName = "Foo", _
    .CompanyAddress = New Address()}

Object initializers allow you to pass any named public property to the constructor of the class. This feature removes the need to create multiple overloaded constructors using different parameter lists to achieve the same goal. The IntelliSense feature of the IDE will help you by displaying a list of the named parameters as you type. You do not have to pass in all of the parameters, and you can use a nested object initializer, as shown in Example 1-4.

Collection Initializers

Initializing collections generally takes up several lines of code as you create the collection, create each object instance, and then add each item one by one to the collection in separate statements. The following statement demonstrates how to initialize a List<Customer> by passing in three instances of a Customer class.

Note

C# supports collection initializers, but VB does not.

C#
List<Customer> custList =
    new List<Customer> { customer1, customer2,  customer3 };

Using the combination of object initializers and collection initializers, you can create a collection and initialize it with a series of objects in a single statement. Using collection initializers, you can create a List<Customer> and add three Customer instances to the List<Customer>, as shown in Example 1-5.

Example 1-5. Collection initializers

C#
List<Customer> custList = new List<Customer>
    {
        new Customer {ID = 1001, CompanyName = "Foo"},
        new Customer {ID = 1002, CompanyName = "Goo"},
        new Customer {ID = 1003, CompanyName = "Hoo"}
    };

Without these features, the syntax to create a List<Customer> and add three new instances of Customer to it is a bit more methodical, as shown in Example 1-6.

Example 1-6. Creating and initializing a collection

C#
Customer customerFoo = new Customer();
customerFoo.ID = 1001;
customerFoo.CompanyName = "Foo";
Customer customerGoo = new Customer();
customerGoo.ID = 1002;
customerGoo.CompanyName = "Goo";
Customer customerHoo = new Customer();
customerHoo.ID = 1003;
customerHoo.CompanyName = "Hoo";
List<Customer> customerList3 = new List<Customer>();
customerList3.Add(customerFoo);
customerList3.Add(customerGoo);
customerList3.Add(customerHoo);

Extension Methods

Extension methods allow you to enhance an existing class by adding a new method to it, without modifying the actual code for the class. For example, a requirement may exist to provide the area of a square surface given the length of one side. You could easily write an extension method to accomplish this. Because all the sides are the same length, you could create an extension method that calculates the square of an integer. An extension method would allow you to expose a method named SquareIt on the int datatype in this case.

You must create extension methods in a static class and define the extension methods as static. The first parameter of the extension method must use the keyword this to signify that the first parameter is the type that the extension method will extend. The code in Example 1-7 uses a static method named SquareIt that accepts a single parameter.

Example 1-7. Extension method

C#
public static class MyExtensions
{
    public static int SquareIt(this int num)
    {
        return num*2;
    }
}
VB
Public Module MyExtensions
    <System.Runtime.CompilerServices.Extension> _
    Public Function SquareIt(ByVal num As Integer) As Integer
        Return num * 2
    End Function
End Module

When you create an extension method, the method appears in IntelliSense in the IDE. Once you have defined the extension method, you can use it to calculate the square of an integer using the code sample in Example 1-8.

Example 1-8. Implementing the extension method

C#
int x = 7;
int squaredX = x.SquareIt();
VB
Dim x As Integer = 7
Dim squaredX As Integer = x.SquareIt()

Extension methods can be a great help; however, I recommend that you use them in situations where they truly do extend a class. For example, I would not create a TrimIt extension method for a string class, because there is already one called Trim. If you wanted to create a method to operate on a Customer class to calculate a customer’s credit limit, a best practice 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 the Customer class. However, extension methods are very useful when you do not have the ability to add a method to the class itself, as in the case of creating a SquareIt method on the int class.

You also should use extension methods when doing so assists a class that you cannot extend because it is part of the .NET Framework. For example, the ADO.NET Entity Framework has a lot of operations that you can perform on its objects. But sometimes you can find yourself performing the same operations repeatedly. I find that it is sometimes convenient to set all of the properties of a class in an entity data model to the modified state. Example 1-9 shows how you can do this using an extension method. A good practice is to organize your extension methods in static classes with other like extension methods.

Example 1-9. Using an extension method to set all of the properties of a class in an entity data model to a modified state

C#
public static class EFExtension
{
   public static void SetAllModified<T>(this T entity, ObjectContext context)
    where T : IEntityWithKey
    {
        var stateEntry =
            context.ObjectStateManager.GetObjectStateEntry(entity.EntityKey);
        var propertyNameList =
            stateEntry.CurrentValues.DataRecordInfo.FieldMetadata.Select
             (pn => pn.FieldType.Name);
        foreach (var propName in propertyNameList)
        {
            stateEntry.SetModifiedProperty(propName);
        }
    }
}
VB
Public Module EFExtension
    <System.Runtime.CompilerServices.Extension> _
    Public Sub SetAllModified(Of T As IEntityWithKey) _
        (ByVal entity As T, ByVal context As ObjectContext)
        Dim stateEntry = _
            context.ObjectStateManager.GetObjectStateEntry(entity.EntityKey)
        Dim propertyNameList = _
            stateEntry.CurrentValues.DataRecordInfo.FieldMetadata. _
                Select(Function(pn) pn.FieldType.Name)
        For Each propName In propertyNameList
            stateEntry.SetModifiedProperty(propName)
        Next propName
    End Sub
End Module

Implicitly Typed Variables

Implicitly typed variables declare the type on which they operate as a specific type based on what they are being set to. You can declare implicitly typed variables using the var keyword (or Dim in VB). The following example shows an implicitly typed variable that represents a string:

C#
var x = "Foo";
VB
Dim x = "Foo"

The variable x becomes a string, because that is the type of the value that it is being set to. As long as the righthand side’s type can be determined, the lefthand side will take on the same type. This syntax can shorten some code samples, too, as shown here with a declaration of a List<T>. There is no need for the redundant List<string>, because the righthand side already shows you the type:

C#
var stringList = new List<string>();
VB
Dim stringList = New List(Of String)()

Anonymous Types/Implicit Types

You can use implicitly typed variables to help create anonymous types, too. Anonymous types allow you to create an object instance that is not of a predefined class type that you have already created. Rather, it is a new and custom class type that you can use within the local scope. When you create an anonymous type, you need to declare a variable to refer to the object. Because you do not know what type you will be getting (because it is a new and anonymous type), you can declare the variable with the var keyword.

For example, you could create an anonymous type that describes a car. The car variable will represent the instance of the class and it will expose the Make, Model, and CoolnessFactor properties. You can use the following code to create this new object without having to create a Car class explicitly. This feature comes in handy when you’re using LINQ to project the results of queries. Example 1-10 demonstrates how to create an instance of a class with Make and Model properties.

Example 1-10. Anonymous type creation

C#
var car = new { Make = "Chevrolet",
    Model = "Camaro", CoolnessFactor = 1 };
VB
Dim car = New With {Key .Make = "Chevrolet", _
    Key .Model = "Camaro", Key .CoolnessFactor = 1}

Anonymous Types and LINQ

When writing a LINQ query expression you may return various pieces of information. Anonymous types are a great way to collect this information within a single object instance. For example, an enumerable list could be queried using LINQ that returns an anonymous type containing the CompanyName from a Customer class and each customer’s city. The City may be from an associated Address class that is a property of the Customer class. Example 1-11 shows one way to accomplish this using an anonymous type.

Example 1-11. Anonymous types with LINQ

C#
var query = from c in customerList
            where c.CompanyAddress.State == "FL"
            select new { Name = c.CompanyName, c.CompanyAddress.City };

foreach (var info in query)
    Console.WriteLine(string.Format("{0} is in {1}, Florida",
        info.Name, info.City));
VB
Dim query = From c In customerList _
    Where c.CompanyAddress.State = "FL" _
    Select New With {Key .Name = c.CompanyName, Key c.CompanyAddress.City}

For Each info In query
    Console.WriteLine(String.Format("{0} is in {1}, Florida", _
        info.Name, info.City))
Next info

The select clause in the LINQ query expression creates an instance of an anonymous type that will have Name and City properties. These values come from the Customer and Address objects. The properties can be renamed (CompanyName is renamed to Name) or they can implicitly take on the name. The resultant anonymous type is available as a local variable in scope.

Get Data-Driven Services with Silverlight 2 now with the O’Reilly learning platform.

O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.