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

  • Download the Information Governance Survey Benchmark Report to gain insights that can help you further establish business value in your Records and Information Management (RIM) program and across your entire organization. Discover how your peers in the industry are dealing with this evolving information lifecycle management environment and uncover key insights such as: 87% of organizations surveyed have a RIM program in place 8% measure compliance 64% cannot get employees to "let go" of information for …

  • Hurricane Sandy was one of the most destructive natural disasters that the United States has ever experienced. Read this success story to learn how Datto protected its partners and their customers with proactive business continuity planning, heroic employee efforts, and the right mix of technology and support. With storm surges over 12 feet, winds that exceeded 90 mph, and a diameter spanning more than 900 miles, Sandy resulted in power outages to approximately 7.5 million people, and caused an estimated $50 …

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds