20

In this question, when mentioning the compiler, I'm actually referring to the Roslyn compiler. The problem arises when using IntelliSense, which is presumed to be the same compiler.

For demonstration purposes and completeness, the following classes are used (using Visual Studio 2015 with C# 6.0 and .NET 4.6.1):

public class A
{
    public IEnumerable<B> B { get; set; }
}
public class B
{
    public IEnumerable<C> C { get; set; }
}
public class C { }
public class Helper<T> { }

Behold the following extension method:

public static void FooBar<T1, T2>(
    this Helper<IEnumerable<T1>> helper,
    Expression<Func<T1, IEnumerable<T2>>> expression) { ... }

The compiler is able to infer it while consuming like this:

Helper<IEnumerable<B>> helper = ...;
helper.FooBar(l => l.C); //T1 is B and T2 is C

Also behold this overloaded extension method:

public static void FooBar<T1, T2, T3>(
    this Helper<T1> helper,
    Expression<Func<T1, IEnumerable<T2>>> expression1,
    Expression<Func<T2, IEnumerable<T3>>> expression2) { ... }

The compiler is NOT able to infer T1 when typing it like this:

Helper<A> helper = ...;
helper.FooBar(l => l. //compiler/IntelliSense cannot infer that T1 is A

This screenshot example will describe more of what I mean with not able to infer:
enter image description here

Also I'm getting this error message when hovering over the extension method with my mouse (I've replaced the < and > characters with [ and ] respectively, because StackOverflow cannot format those in a quote):

The type arguments for method 'FooBar[T1,T2](this Helper[IEnumerable[T1]], Expression[Func[T1, IEnumerable[T2]]])' cannot be inferred from the usage. Try specifying the type arguments explicitly.

But when completing it manually like this:

helper.FooBar(l => l.B, l => l.C); //compiler infers that T1 is A, T2 is B and T3 is C

the compiler is happy.

Why can't the compiler/IntelliSense (or the autocomplete feature of Visual Studio) figure out T1 and wants me to specify the type arguments explicitly when I start typing?

Note that if I omit IEnumerable<> everywhere in my examples, the compiler can happily infer everything while typing.
The compiler is also happy after you manually type in l => l.B. It then knows T1 is A, so you can express the last argument with the help of IntelliSense.

leppie
  • 115,091
  • 17
  • 196
  • 297
QuantumHive
  • 5,613
  • 4
  • 33
  • 55
  • 9
    when you say "compiler cannot infer that T1 is A" you actually mean the IDE's auto completion feature, right? – Tim Pohlmann Jul 13 '16 at 12:27
  • @TimPohlmann I've edited my question, concealing the irrelevant part. And yes, I mean IntelliSense auto completion feature does not work at that very specific moment. – QuantumHive Jul 13 '16 at 12:35
  • 2
    One of the ideas behind the whole Roslyn project was to have one single compiler that both compiled the code and ran in IntelliSense. For some way however, IntelliSense is a little different than compiling stuff (I think more information is needed). So we still have some cases where IntelliSense just bails our, while code still happily compiles :-( – Steven Jul 13 '16 at 14:21
  • @Steven so are you proposing that this is actually a bug in IntelliSense? – QuantumHive Jul 13 '16 at 14:26
  • 2
    I would more say it is a limitation 'by design' :) – Steven Jul 13 '16 at 14:26
  • What kind of answer do you expect here? The kind of "It has not been implemented because there is too much nesting and it's rarely a use case"? Or "Roslyn wasn't optimized to understand incomplete code and this one had been left out"? What do you do if you get such an answer? – Stefan Steinegger Jul 15 '16 at 13:04
  • 1
    @StefanSteinegger I expect a **confirmation** from experts on if this is a legit bug, or that this is an *intentional* limited feature by design. – QuantumHive Jul 15 '16 at 13:06
  • 2
    I had a similar problem http://stackoverflow.com/questions/34883559/intellisense-cannot-infer-type-from-extention-method I've created a bug report https://connect.microsoft.com/VisualStudio/feedback/details/2263258/vs-2015-intellisense-cannot-infer-type-from-extention-method – George Vovos Jul 18 '16 at 16:19

3 Answers3

4

If I understood you correctly, everything is working as expected for me in VS2013:

Your first case:

Your second case:

I'm starting to type the l. and IntelliSense shows me that l has a property B that can be used. So if I'm right and it infers correctly in VS2013 whereas it doesn't infer in VS2015, then it's definitely a bug in VS2015 IntelliSense that can be reported to Microsoft.

Ivan Yurchenko
  • 3,762
  • 1
  • 21
  • 35
2

I found your question interesting and since I'm using VS 2015 I thought I could try it too. I got the same error as you did, so i guess it can be a VS bug, since in other versions it is working properly.

Here is my error:

enter image description here

And I also get No suggestions on CTRL + SPACE.

EDIT:

A similar bug can be seen here.

So, since in older versions of VS this works, then this is the reason why it can be considered a bug.

Community
  • 1
  • 1
meJustAndrew
  • 6,011
  • 8
  • 50
  • 76
2

As I said I had the same problem. (And here is the bug report)
It is not related to Expressions or IEnumerable.
Here is a simplified example

using System;

namespace ConsoleApplicationExpressionTree
{

    public static class Extentions
    {
        static void Main() { }
        static void AMethod(string[] args)
        {
            SetValue(new Customer(), e => e.Name   /*type here */, "TheName");

        }

        public static void SetValue<TEntity, TProperty>(TEntity instance, Func<TEntity, TProperty> expression, TProperty newValue)
        {

        }
    }
    public class Customer
    {
        public string Name { get; set; }
        public int Age { get; set; }
    }
}

I just noticed that if you remove the 3rd argument from the method it works !!!

using System;

namespace ConsoleApplicationExpressionTree
{

    public static class Extentions
    {
        static void Main() { }
        static void AMethod(string[] args)
        {
            SetValue(new Customer(), e => e.Name   /*type here */);

        }

        public static void SetValue<TEntity, TProperty>(TEntity instance, Func<TEntity, TProperty> expression)
        {

        }
    }
    public class Customer
    {
        public string Name { get; set; }
        public int Age { get; set; }
    }
}

So if you are very lucky you might be able to fix your current example by tweaking your parameters.
If not,you'll just have to wait for MS to fix it...

Community
  • 1
  • 1
George Vovos
  • 7,563
  • 2
  • 22
  • 45