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.
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.
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.
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 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 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)()
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.
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.