Quantcast
Channel: Martijn's C# Programming Blog » LINQ
Viewing all articles
Browse latest Browse all 2

C# Anonymous types: the Basics

$
0
0

With the introduction of .NET 3.5 C# includes the “var” keyword to support anonymous types. One important motivation for this was to make code written with LINQ (Language-Integrated Query) easier to read. So what is an anonymous type? Anonymous types simply mean that you don’t specify the type — but let the compiler do it instead.

To understand how LINQ works, and to realize that its more than just syntactic sugar added to C# we first need to review a couple of basic concepts that when combined lead to LINQ.

Simple definitions, simplified

In typical C# you will always carefully spell out your definitions:

string MyString = “Hello World”;

From the right side of the declaration it is obvious that only one type (string) will ever match this definition. So instead of us doing the work, why not let the compiler figure this out?

var MyString = “Hello World”;

The above definition will also create a string variable named “MyString”. It is important to note that C# is still strongly typed — unlike scripted languages such as Visual Basic (or PHP) once you have assigned the variable type it sticks. The following will not work:

var MyString2 = “Hello World”;
MyString2 = 123; // Nice try, but no banana

The compiler will throw an implicit conversion error as 123 cannot be assigned to a string.

The above was an impressive (if somewhat pointless) example of what an anonymous type is. For simple types such as strings, integers etc anonymous types offer little benefits. It is even possible to argue that it reduces your code readability.

Using anonymous types for creating arrays

We are not limited to simple types – the following example show how we can create an anonymous array of integers:

int[] myIntArray = new int[] { 1 , 2 , 3 , 4 , 5 , 6 };
var myIntArrayVar = new [] { 1 , 2 , 3 , 4 , 5 , 6 };

The above two examples are equivalent. You still need to specify that you want to create an array using new. You are however limited by how flexible the compiler is. A mixed array is a little too much and we need to force it down to an object array:

var myMixedArray = new [] { 1, “two” , 3 , “four” , 5 }; // this does NOT work
object[] myMixedArray = new object[] { 1, “two” , 3 , “four” , 5 }; // this is OK

Saving time and code with anonymous types

One very powerful feature of anonymous types it that they can save you a lot of work. Instead of having to type out everything explicitly, you can have the compiler infer the definition of a complete class:

using System;
class MainClass
{
        public static void Main(string[] args)
        {
            var Person = new { Name = "Michael", Age = 23 };
            Console.WriteLine("Name: {0} Age: {1}",Person.Name,Person.Age);
        }
}

The compiler creates an anonymous definition of a class and assigns to it Name and Age as public fields. We can combine anonymous classes and anonymous arrays to create an Employee data structure. Note that we use a “Var” in the ForEach loop. Because we are combining anonymous types it is impossible to determine the correct type to loop over each element in the array. Thankfully we can ask the compiler to fill in the correct iterator type for us:

    using System;
    class MainClass
    {
        public static void Main(string[] args)
        {
            var Person1 = new { Name = "Michael", Age = 23 };
            var Person2 = new { Name = "Sandra", Age = 33 };
            var Employees = new [] { Person1, Person2 };

            foreach (var P in Employees)
                Console.WriteLine("Name: {0} Age: {1}",P.Name,P.Age);

        }
    }

As Employees is an array we can use an iterator to step through it as shown above, but we can just as easily use a for loop:

    using System;
    class MainClass
    {
        public static void Main(string[] args)
        {
            var Person1 = new { Name = "Michael", Age = 23 };
            var Person2 = new { Name = "Sandra", Age = 33 };
            var Employees = new [] { Person1, Person2 };

            for(int Lp = 0; Lp < Employees.Length; Lp++)
              Console.WriteLine("Name: {0} Age: {1}",Employees[Lp].Name,Employees[Lp].Age);
        }
    }

Lambda expressions and anonymous methods

Lambda expressions are an extension of Anonymous Methods which were introduced in C# 2.0 Lambda expressions add a lot of power to C#, but they can be hard to understand. They are however great for small filters than can be passed on to functions as variables as we will see in the following example:

Func<int,bool> mySeniorStaffFilter = a => a > 35;
Console.WriteLine(“36 is senior? {0}”,mySeniorStaffFilter(36));

The above function can be defined anywhere inside your code. It takes a single integer as a parameter and returns a boolean. The integer is defined as “a”, and the function checks whether a is larger than 35.

Using the “old” anonymous methods we could have written the above as:

Func<int,bool> mySeniorStaffFilter = delegate(int a) { return a > 35; };

The Lambda expression syntax is the same, but shorter and this make them slightly easier to understand.

Combining Lambda expressions with Queries

If you have used Intellisense in Visual Studio or the greatly improved object browser in MonoDevelop 2.0 you will discover that with C# 3.0 arrays have a much larger set of methods associated with them. One of these methods is “Where”. It takes a filter as an argument ; and then simply applies each member of the array to the filter. If the filter returns “true” the value is yielded and returned in a sub-set.

In plain language: If you have an array of employees you can pass mySeniorStaffFilter function to the Where method. It checks every staff member to see if their age is over 35 and then returns an array containing only those members of staff.

var SeniorStaff = Employees.Where(s => s.Age > 35);

The following code example shows how this works:

   using System;
   using System.Linq;

    class MainClass
    {
        public static void Main(string[] args)
        {
            var Employees = new [] { new { Name = "Michael", Age = 23 },
                                     new { Name = "Anton", Age = 43 },
                                     new { Name = "Nadia", Age = 36 },
                                     new { Name = "Lydia", Age = 34 } };

            var SeniorStaff = Employees.Where(s => s.Age > 35);          

            foreach(var Member in SeniorStaff)
             Console.WriteLine("Name: {0} Age: {1}",Member.Name,Member.Age);

        }
    }

From the above you can see that the compiler needs to infers,create and apply several anonymous types to create a working code sample. We thankfully can use “var” to specify our SeniorStaff sub-set. Behind the scenes the compiler has created an anonymous IEnumberable for SeniorStaff that contains our Employee records with anonymous staff members. And for the Where method it applied an anonymous delegate to create the correct filter.

And this leads us to the first real “LINQ” statement.

var SeniorStaff = from S in Employees where S.Age > 35 select S;

When you take away the syntactic sugar the C# compiler will build from this the same (or very similar) “Employees.Where(s => s.Age > 35)” statement. Using the same anonymous methods, variables and functions as we used earlier.

This took a lot of getting to. It is however important to understand that LINQ is implemented in C# through basic, strongly typed variables and methods.

To keep the complexity of our C# code down the “var” keyword was added allowing the programmer to focus on coding the logic of the program instead of having to write long hand type definitions for each variable.

Q. Is var typesafe?

There’s no concern about type-safety, as the variable created is not dynamic. It’s just compiler magic and any type unsafe calls you make will get caught.

Q. Is there a performance penalty for using “var” ?

The var keyword only tells the compiler to infer the type from the assignment, there’s no runtime difference, so no, there’s no penalty performance wise.

Q. On using anonymous definitions in large projects

Anonymous types cannot be shared across assembly boundaries. The compiler ensures that there is at most one anonymous type for a given sequence of property name/type pairs within each assembly. To pass structures between assemblies you will need to properly define them.

References

1. http://msdn.microsoft.com/en-us/library/bb308959.aspx

This is a post from Martijn's C# Coding Blog.


Viewing all articles
Browse latest Browse all 2

Latest Images

Trending Articles





Latest Images