dmck
dmck

Reputation: 7861

Visual Studio bug resolving lambda in method with Func delegate overloads?

I've come across some strange behaviour in Visual Studio 2010 when using anonymous methods in functions that have overloads of various Func delegates.

I've created a small reproduction class below.

Consider this ListViewAdapter class

namespace LambdaTestApp
{
    public class ListViewAdapter<T>
    {
        private Func<int, string, int, string> _converter1;
        private Func<RefType1, string, string> _converter2;

        public ListViewAdapter(int arg1, Func<int, string, int, string> converter) 
        {
            _converter1 = converter;
        }

        public ListViewAdapter(int arg1, Func<RefType1, string, string> converter) 
        {
            _converter2 = converter;
        }

        public static ListViewAdapter<T> MockPopulate(int arg, Func<int, string, int, string> converter) {

            ListViewAdapter<T> instance = new ListViewAdapter<T>(arg, converter);

            return instance;
        }

        public static ListViewAdapter<T> MockPopulate(int arg, Func<RefType1, string, string> converter)
        {
            ListViewAdapter<T> instance = new ListViewAdapter<T>(arg, converter);
            return instance;
        }
    }

    public class RefType1
    {
        public string Property1 { get; set; }
    }
}

And this following code that uses the overload with a lambda:

namespace LambdaTestApp
{
    class Program
    {
        static void Main(string[] args)
        {
            ListViewAdapter<RefType1>.MockPopulate(1, (item, str) =>
            {
                var myItem = item;
                return str;
            });
        }
    }
}

It should resolve to Func<RefType1, string, string> and the first argument should be RefType1, however the issue is that instead of item being a RefType1 Visual Studio sees it as an int.

Question: Is there a valid conversion between the Func delegates that isn't obvious, or is this a Visual Studio IntelliSense bug?

Upvotes: 4

Views: 498

Answers (3)

Jon Skeet
Jon Skeet

Reputation: 1503509

I would personally agree that it's a bug - or at least a flaw. Even though it only shows up when the code is invalid - and it's only when the code within the lambda expression is invalid - I think it would be more appropriate for Intellisense to view the parameter declaration part of the lambda expression (the part before =>) as complete, and work based on that information.

There's nothing you can put within the body of the lambda expression which would change the overload resolution in a way which would make the choice of Func<int, string, int, string> valid... there just aren't enough parameters.

I would at least suggest logging a Connect issue about this - it may well be "working as intended" in terms of "MS didn't design it to cope with this scenario" but it could clearly work better. Note that it's still working this way in the VS11 beta, although admittedly the laptop I'm running on at the moment hasn't applied the recent update to the beta. I wouldn't be surprised if you got a response which amounted to "Yes, that would be nice - but it would take a huge amount of work for little gain" but it's worth raising anyway, IMO.

Upvotes: 5

user743382
user743382

Reputation:

It should resolve to Func<RefType1, string, string> and the first argument should be RefType1, however the issue is that instead of item being a RefType1 Visual Studio sees it as an int.

Visual Studio guesses it as an int as long as there is a compilation error, by looking at the first of the overloaded functions. It cannot determine which overload is used, because it cannot understand the code. It might be able to do a better job at guessing which one you probably meant, but it's a bit harsh to consider this a bug.

When you fix the error, Visual Studio can tell that the argument type is RefType1. You can verify this by making sure the code compiles, and then hovering the mouse over item.

Yes, unfortunately that means that since typing . will at least temporarily make the code invalid, and will display the members of int. If you really need to, you can call item.ToString(); in a way that satisfies the compiler, and then overwrite the . with a new .. You'll then see the list you're hoping for.

Alternatively, make sure your most commonly used overload is listed first, to make Visual Studio guess differently.

Upvotes: 2

Mike Hofer
Mike Hofer

Reputation: 17022

Method overload resolution for LINQ works exactly like it does for C#: based on number and types of arguments, and nothing more. Whether or not an argument is generic is irrelevant.

Without seeing the code in PopulateListView, I'd have to say that you're passing an int, and that's what you're getting.

Upvotes: 0

Related Questions