Starting with version 3.0.0.3010, the C# code completion in SharpDevelop has support
for implicitly typed lambda expressions.
Given a variable "IEnumerable<MyClass> items" and the "Select" extension method
from LINQ, typing "items.Select(i => i." now shows the members of MyClass. And
if the result of the Select call is assigned to an implicitly typed variable, SharpDevelop
is now able to infer that the variable has the type IEnumerable<return type of
the lambda expression>.
Unlike all other expressions in C#, the type of a lambda expression cannot be inferred
just from by at the expression itself (and the variables used by the expression).
To resolve lambda type parameters, we also need to look at the context where
the lambda is used. Currently, not all contexts are supported by code-completion,
you can find the list
of known problems in our bugtracker (Component: DOM / Resolver). Should you find
anything where code-completion does not work correctly which is not in that list,
please file a bug report in our forum.
The most commonly used context for lambda expressions is method calls, and this is
also the most difficult thing to support. It's easy when the method has a clear
signature like "void M(Func<int, string> f)", since then SharpDevelop can infer
the lambda parameter types directly from the delegate type. But most of the
time, things aren't that easy. For example, the signature of the Select method is
"IEnumerable<R> Select<T, R>(this IEnumerable<T> input, Func<T,
R> f)". Here, SharpDevelop needs to first infer what T is, then it can know what
the lambda parameter types are, and only after that it can resolve the lambda expression
to infer what R is.
But when the method has multiple overloads, things can get even more messy:
When a method has to overloads "void M(Func<string, int>
f)" and "void M(Func<int, int> f)", it
is valid to call them like this: "F(i=>i.Length)",
"F(i=>i+i)". In the first call, i is
a string; in the second, it is int. What SharpDevelop needs to do here is to infer
the lambda parameter types for each overload separately, infer the lambda's return
type; and then check that against the delegate's signature to see which overload was
the correct one.
i=>i.Length is a resolve error if i would
be int, but returns the expected int if i is
string; so i must resolve to string.
i=>i+i returns a string if i would
be string, but returns the expected int if i is
int; so i must resolve to int.
Note that because there's no way to tell the type of i before
the lambda expression is completed, you cannot expect that SharpDevelop gives you
correct code completion for it. "Length" will not be included in the code-completion
list for i when you type ".", because at that
point, the method call is "F(i=>i)", and i is
thus an int. But after the expression is written, SharpDevelop will show a tooltip
for "Length", and features like "Go to definiton" and "Find references" will work.