Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Compile-time error with LINQ Select on IEnumerable<dynamic>

Tags:

Please see further down for major update!


I have some code like this:

void Test(IEnumerable x) {   var dynX = x.Cast<dynamic>();   var result = dynX.Select(_ => _.Text); } 

in an existing library project targeted at .NET 4.5. VS2015’s IntelliSense underlines the Text part, complaining that: 'object' does not contain a definition for 'Text'...

Sure enough, compiling fails with

error CS1061: 'object' does not contain a definition for 'Text' and no extension method 'Text' accepting a first argument of type 'object' could be found (are you missing a using directive or an assembly reference?)

This message always says 'object', even when I change the cast to .Cast<IAsyncResult>() or whatnot. When I hover the lambda parameter, the tooltip shows it’s of type IColumn (which exists but is unrelated). Again, no matter what type I cast to.

However, when I hover the Select() method, it correctly shows the parameter as Func<dynamic, dynamic>. If I specify the lambda parameter type explicitly, it compiles. If I specify the type parameters on Select() explicitly, it works, too.

Other usages of LINQ with dynamic are working. When I copy this method to another (existing) project in the solution, it compiles, too. When I copy it to another file in the same project, it does not compile.

It compiles with VS2013, too.

The very same error appears for all my colleagues as well, both in Windows 8.1 and Windows 10.

Perhaps this is some odd problem with type inference...?

Things I’ve tried that didn’t help:

  • Create a new .NET 4.5 library project and re-add files and missing references
  • Compare (original) project files—no differences except element ordering

Update

Well, I managed to create a self-contained minimal failing example:

static class Program {     static void Main(string[] args)     {         IEnumerable x = new object[0];         IEnumerable<dynamic> dynX = x.Cast<dynamic>();          // CS1061 'object' does not contain a definition for 'Text'...         // var tooltip shows IColumn instead of IEnumerable<dynamic>         var result = dynX.Select(_ => _.Text);     }      public static IColumn Select<TResult>(this IColumn source, Func<object, TResult> selector)     {         throw new NotImplementedException();     } }  public interface IColumn { } 

From how I see it, this clearly indicates there’s a serious bug in how VS2015/the new compiler version resolves extension methods.


The following is only loosely related and is mainly about misleading error messages. I decided to leave it to not make comments confusing.

Even worse, these also fail with the same error, even though neither IEnumerable nor object could possibly have an extension method Select():

// CS1061 'object' does not contain a definition for 'Text' // var tooltip shows IColumn var result2 = x.Select(_ => _.Text);  object o = new object();    // CS1061 'object' does not contain a definition for 'Text' // var tooltip shows IColumn var result3 = o.Select(_ => _.Text); 

Addendum

This issue is now tracked on the Roslyn bug tracker.

like image 885
Daniel B Avatar asked Jul 29 '15 12:07

Daniel B


1 Answers

cant speak to why it works in one VS and not the Other but this is what i would do

rename.

public static IColumn Select<TResult>(this IColumn source, Func<object, TResult> selector)     {         throw new NotImplementedException();     } 

"Select" is a build in Method Name for other common lib. so I highly suggest you rename it to something like "AppColumnSelectText" or what ever but not "Select".

then change

public interface IColumn { } 

To

public interface IColumn  {     string Text {get; set;} } 

then implement it

public class MyClass : IColumn {    public string Text { get; set;} } 

Then cast your dynamic to IColumn instead, assuming it come from MyClass class type

var columns = fields.Select(a => new {a as IColumn}).ToList(); 

Then your will be able to do

var result3 = columns.AppColumnSelectText(x => x.Text); 

I know this is probably not what you are looking for but the programming is cleaner and you can archive the same result.

UPDATE

Please read the below and comments hopefully this paints a better picture.

public static class Test {     public static IColumn Select<TResult>(this IColumn source, Func<object, TResult> selector)     {         throw new NotImplementedException();     }      public static IColumn SelectOtherColumn<TResult>(this IColumn source, Func<IColumn, TResult> selector)     {         throw new NotImplementedException();     } }  public interface IColumn {     string Text { get; set; } }  public class Program {     private static void Main(string[] args)     {         IEnumerable ojb = new object[0];         IEnumerable<dynamic> dynX = ojb.Cast<dynamic>();          // CS1061 'object' does not contain a definition for 'Text'...         // var tooltip shows IColumn instead of IEnumerable<dynamic>          //NB this is the System.Linq.Select         var result = dynX.Select(x => x.Text);          var xzy = dynX as IColumn;         //converstion here will probably FAIL so this makes this pointless.           //here the compliter complains as the Type object has no Text Prop as it was not sepcified anywhere         var theThingyouwant1 = xzy.Select(x => x.Text);          //here you are OK as the complier can infer something         var theThingyouwant2 = xzy.SelectOtherColumn(x => x.Text);     }  } 

Update Further to this... See below for illustration perposes

public class MyType {     public string  Text { get; set; }     public string  SomethingEsle { get; set; } }  public class Program {     private static void Main(string[] args)     {         List<MyType> ojb = new List<MyType>();         ojb.Add(new MyType {Text = "OMG", SomethingEsle = "cat"});          //dynX is a dynamic...         var dynX = ojb.Select(x => new {x.Text, x.SomethingEsle}).ToList();          //NB this is the System.Linq.Select         //this now works as the complier can determine that there is a property Text         var result = dynX.Select(x => x.Text).ToList();      } } 

You could use your Select... but it only works on something which implements IColumn, which is why i find it hard to see how it would ever work.

like image 179
Seabizkit Avatar answered Oct 26 '22 17:10

Seabizkit