Go Inside C# 3.0's Type Inference Process

Lambda expressions in C# 3.0, which are a simpler syntax for representing anonymous methods, can have either explicitly typed parameters (where the type of each parameter is explicitly stated) or implicitly typed parameters (where the type of each parameter is inferred from the context in which the lambda expression occurs). In the case of implicitly typed parameters, when the lambda expression is converted to a compatible delegate type, it is this delegate type that provides the parameter types.

In a scenario when a generic method is called without specifying type arguments, a type inference process attempts to infer the arguments. The lambda expressions passed as arguments to the generic method participate in this inference task. Initially, the type inference occurs independently for each argument. It takes place in two stages:

  1. It infers nothing from arguments that are lambda expressions.
  2. It uses an iterative process to makes inferences from lambda expressions.

The iterative process continues until all the following conditions are valid for any given argument:

  • The argument is a lambda expression (L), from which no inferences have been made.
  • The corresponding parameter's type (P) is a delegate type, which has a return type that involves one or more method type parameters.
  • Both L and P have the same number of parameters, and the same modifiers if they're explicit or no modifiers if L has an implicitly typed parameter list.
  • P's parameter types involve no method type parameters or involve only method type parameters for which a consistent set of inferences has already been made.
  • If L has an explicitly typed parameter list, when inferred types are substituted for method type parameters in P, each parameter in P has the same type as the corresponding parameter in L.
  • If L has an implicitly typed parameter list, when inferred types are substituted for method type parameters in P and the resulting parameter types are given to the parameters of L, the body of L is a valid expression or statement block.
  • A return type then can be inferred for L, as described.

For each such argument, the return type of P is related to the inferred return type of L, and the inferred type derived is added to the list of inferences. This process repeats until no further inferences can be made.

This article demonstrates how type inference works in C# 3.0.

Type Inference Example

To understand how the type inference process works, consider the Select extension method declared in the following System.Query.Sequence class:

namespace System.Query
{
   public static class Sequence
   {
      public static IEnumerable<S> Select<T,S>(
         this IEnumerable<T> source,
         Func<T,S> selector)
      {
         foreach (T element in source) yield return selector(element);
      }
   }
}

Now, create a new Visual C# LINQ Console project and type in the following code:

//Program.cs
using System;
using System.Collections.Generic;
using System.Text;
using System.Query;
using System.Xml.XLinq;
using System.Data.DLinq;

namespace TypeInference
{
   class Program
   {
      static void Main(string[] args)
      {
         List<int> intList = new List<int>();
         intList.Add(1);
         intList.Add(2);
         intList.Add(3);
         intList.Add(4);
         intList.Add(5);
         intList.Add(6);
         intList.Add(7);
         intList.Add(8);
         intList.Add(9);
         intList.Add(10);
         IEnumerable<int> remainderList = intList.Select(c => c%2 );
         foreach(int i in remainderList)
         {
            Console.WriteLine(i);
         }
         Console.ReadLine();
      }
   }
}

To compile the code, you need the LINQ Preview released at PDC 2005. If you do not have Visual Studio 2005, you can compile it with the console by using the following command:

C:\Program Files\LINQ Preview\Bin\Csc.exe
   /reference:"C:\Program Files\LINQ Preview\Bin\
                  System.Data.DLinq.dll"
   /reference:C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\
                 System.Data.dll
   /reference:C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\
                 System.dll
   /reference:"C:\Program Files\LINQ Preview\Bin\System.Query.dll"
   /reference:C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\
                 System.Xml.dll
   /reference:"C:\Program Files\LINQ Preview\Bin\System.Xml.XLinq.dll"
   /out:obj\Debug\TypeInference.exe /target:exe Program.cs

The above code prints the remainder of the integer list when each integer in the list is divided by two.

When the C# compiler encounters the following snippet:

IEnumerable<int> remainderList = intList.Select(c => c%2 );

It converts it to the following:

IEnumerable<int> remainderList = Sequence.Select(intList, c => c%2);

Because type arguments were not explicitly specified, type inference came into play. First, the intList argument is related to the source parameter, inferring T to be int. Then, by using the lambda expression type inference process, the code gave c the type int. Thus, the invocation was equivalent to the following:

Sequence.Select<int, int>(intList, (int c) => c%2

And the result is of type IEnumerable<int>.

About the Author

Vipul Patel is a Microsoft MVP (two years in a row) in Visual C# and currently works at Microsoft through Volt Information Sciences. He specializes in C# and deployment issues. You can reach him at Vipul_d_patel@hotmail.com.



About the Author

Vipul Vipul Patel

Vipul Patel is a Software Engineer currently working at Microsoft Corporation, working in the Office Communications Group and has worked in the .NET team earlier in the Base Class libraries and the Debugging and Profiling team. He can be reached at vipul_d_patel@hotmail.com

Downloads

Comments

  • I don't understand

    Posted by Magallo on 05/04/2006 04:27am

    I didn't understand the last passage. I mean, I understand the passage from IEnumerable remainderList = intList.Select(c => c%2 ); to IEnumerable remainderList = Sequence.Select(intList, c => c%2);
    But, what is c? How the lambda expressions work? I can't understand it. And above all, how can be finally Sequence.Select(customers, (Customer c) => c.Name)? What is Customer type? Where is string type? What a mess!

    Reply
Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • Available On-Demand Today's changing workforce dynamics, economic challenges, and technological advances are placing immense pressure on business leaders to turn their focus on people – their most valuable asset – in order to remain competitive. Research shows that a significant number of new employees quit within one year of taking a new job*. Whether it's through a merger and acquisition, or standard hiring process, like any first impression, early experiences shape their opinions of their new …

  • Managing your company's financials is the backbone of your business and is vital to the long-term health and viability of your company. To continue applying the necessary financial rigor to support rapid growth, the accounting department needs the right tools to most efficiently do their job. Read this white paper to understand the 10 essentials of a complete financial management system and how the right solution can help you keep up with the rapidly changing business world.

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds