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:

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>();
         IEnumerable<int> remainderList = intList.Select(c => c%2 );
         foreach(int i in remainderList)

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\
   /reference:"C:\Program Files\LINQ Preview\Bin\System.Query.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



  • 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!

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

Top White Papers and Webcasts

  • Java developers know that testing code changes can be a huge pain, and waiting for an application to redeploy after a code fix can take an eternity. Wouldn't it be great if you could see your code changes immediately, fine-tune, debug, explore and deploy code without waiting for ages? In this white paper, find out how that's possible with a Java plugin that drastically changes the way you develop, test and run Java applications. Discover the advantages of this plugin, and the changes you can expect to see …

  • On-demand Event Event Date: September 10, 2014 Modern mobile applications connect systems-of-engagement (mobile apps) with systems-of-record (traditional IT) to deliver new and innovative business value. But the lifecycle for development of mobile apps is also new and different. Emerging trends in mobile development call for faster delivery of incremental features, coupled with feedback from the users of the app "in the wild." This loop of continuous delivery and continuous feedback is how the best mobile …

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds