FREE BOOK

Chapter 8: C# 4.0 Features

Posted by Addison Wesley Free Book | C# Language February 02, 2010
This chapter looks at the new features added into C# 4.0 that combine to improve code readability and extend your ability to leverage LINQ to Object queries over dynamic data sources.

Using Named and Optional Arguments in LINQ Queries

Named and optional arguments offer an alternative way to reduce code in LINQ queries, especially regarding flexibility in what arguments can be omitted in an object constructor.

Although anonymous types make it convenient to project the results of a query into an object with a subset of defined properties, these anonymous types are scoped to the local method. To share a type across methods, types, or assemblies, a concrete type is needed, meaning the accumulation of simple types or constructor methods just to hold variations of data shape projections. Object initializers reduce this need by allowing a concrete type to have a constructor without parameters and public properties used to assign values in the Select projection. Object-oriented purists take issue with a parameterless constructor being a requirement; it can lead to invalid objects being created by users who are unaware that certain properties must be set before an object is properly initialized for use-an opinion I strongly agree with (you can't compile using the object initialize syntax unless the type concerned has a parameterless constructor, even if there are other constructors defined that take arguments).

Optional and named arguments can fill this gap. Data can be projected from queries into concrete types, and the author of that concrete type can ensure that the constructor maintain integrity by defining the default values to use when an argument is omitted. Many online discussions have taken place discussing if this is a good pattern; one camp thinks it doesn't hurt code readability or maintainability to use optional arguments in a constructor definition, and the other says refactoring makes it an easy developer task to define the various constructors required in a given type, and hence of no value. I see both sides of that argument and will leave it up to you to decide where it should be employed.

To demonstrate how to use named and optional arguments from a LINQ query, the example shown in Listing 8-3 creates a subset of contact records (in this case, contacts from California) but omits the email and phone details. The Console window output of this example is

Gottshall, Barney (10/19/1945) - CA
Deane, Jeffery (12/16/1950) - CA
Listing 8-3

Example LINQ query showing how to use named arguments and optional arguments to assist in projecting a lighter version of a larger type

var q = from c in Contact.SampleData()
        where c.State == "CA"
        select new Contact(
            c.FirstName, c.LastName,
            state: c.State,
dateOfBirth: c.DateOfBirth
);

foreach
(var c in q)
    Console.WriteLine("{0}, {1} ({2}) - {3}",
        c.LastName, c.FirstName,

        c.DateOfBirth.ToShortDateString(), c.State);


public class Contact
{
        // Constructor defined with optional arguments
        public Contact(
            string firstName,
            string lastName,
            DateTime dateOfBirth,
            string email = "unknown", // optional
            string phone = "", // optional
            string state = "Other") // optional
        {
            FirstName = firstName;
            LastName = lastName;
            DateOfBirth = dateOfBirth;
            Email = email;
            Phone = phone;
            State = state;
        }
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public string Email { get; set; }
        public string Phone { get; set; }
        public DateTime DateOfBirth { get; set; }
        public string State { get; set; }
        public static List<Contact> SampleData() ...
        // sample data the same as used in Table 2-1.

}

Total Pages : 11 45678

comments