LINQ上的编译时错误选择IEnumerable <dynamic>

时间:2015-07-29 12:14:21

标签: c# dynamic extension-methods visual-studio-2015 type-inference

请进一步了解主要更新!

我有一些像这样的代码:

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

在针对.NET 4.5的现有库项目中。 VS2015的IntelliSense强调了Text部分,抱怨说:'object'不包含'Text'的定义......

果然,编译失败了

  

错误CS1061:'object'不包含'Text'的定义,也没有扩展方法'Text'可以找到接受'object'类型的第一个参数(你是否缺少using指令或汇编引用?)

此消息总是显示'object',即使我将演员表更改为.Cast<IAsyncResult>()或诸如此类的东西。当我将lambda参数悬停时,工具提示显示类型IColumn(存在但不相关)。再说一遍,无论我演绎什么类型。

但是,当我将Select()方法悬停时,它会正确显示参数Func<dynamic, dynamic>。如果我明确指定lambda参数类型,它将编译。如果我明确指定Select()上的类型参数,它也可以。

LINQ与dynamic的其他用法正在发挥作用。当我将此方法复制到解决方案中的另一个(现有)项目时,它也会编译。当我将它复制到同一项目中的另一个文件时,它不会编译。

它也与VS2013一起编译。

在Windows 8.1和Windows 10中,所有同事都会出现同样的错误。

也许这是类型推断的一些奇怪问题......?

我尝试的事情没有帮助:

  • 创建新的.NET 4.5库项目并重新添加文件和缺少参考
  • 比较(原始)项目文件 - 除元素排序之外没有差异

更新

好吧,我设法创建了一个自包含的最小失败的示例:

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 { }

从我的看法来看,这清楚地表明VS2015 /新编译器版本如何解决扩展方法存在严重错误。

以下内容只是松散相关,主要是关于误导性的错误消息。我决定不让评论混淆。

更糟糕的是,即使IEnumerableobject都没有可能有扩展方法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);

附录

现在可以在Roslyn bug tracker上跟踪此问题。

4 个答案:

答案 0 :(得分:1)

无法说明为什么它在一个VS而不是另一个VS中起作用,但这就是我要做的事情

重命名。

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

&#34;选择&#34;是其他常见lib的方法名称的内置版本。 所以我强烈建议你将它重命名为&#34; AppColumnSelectText&#34;或者什么,但不是&#34;选择&#34;。

然后改变

public interface IColumn { }

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

然后实现它

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

然后将动态转换为IColumn,假设它来自MyClass类类型

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

然后你就能做到

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

我知道这可能不是你想要的,但编程更清晰,你可以存档相同的结果。

更新

请阅读以下内容并发表评论,希望这能描绘得更好。

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);
    }

}

<强>更新 更进一步......见下面的插图

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();

    }
}

你可以使用你的Select ...但它只适用于实现IColumn的东西,这就是为什么我很难看出它是如何工作的。

答案 1 :(得分:0)

您可以使用此

 static void Main(string[] args)
    {
        //IEnumerable x = new object[0];
        //var result2 = x.Select(_ => _.Text); 
        //Compile time Error "Enumerable" does not contain a definition for  'Select' and no extension method
       // because IEnumerable is not a generic class

        IEnumerable<object> x = new object[0];
        var result2 = x.Select(_ => _.Text);        
    }

答案 2 :(得分:0)

这只是一种情况,c#使用一种鸭子类型来允许LINQ适用于任何类型。

我将从一个简单的例子开始。

如果我定义了一个类Foo行:

public class Foo
{
    public int Bar;
    public int Select(Func<Foo, int> map)
    {
        return map(this);
    }
}

然后我可以编写这段代码:

Foo foo = new Foo() { Bar = 42 };

int query =
    from f in foo
    select f.Bar;

我得到的LINQ版本适用于返回Foo(不是IEnumerable<T>)的int类型(不是IEnumerable<R>)。

可以通过这种方式专门定义所有LINQ运算符。

上面的例子也可以写成:

public class Foo
{
    public int Bar;
}

public static class Ex
{
    public static int Select(this Foo source, Func<Foo, int> selector)
    {
        return selector(source);
    }
}

现在它看起来像你的代码。

所以如果我们做出这个改变:

public class Foo
{
}

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

public interface IColumn { }

然后此代码失败并出现与您相同的错误:

IEnumerable<dynamic> foo = new [] { new Foo() };

var query =
    from f in foo
    select f.Text;

如果我再做这个改变:

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

现在代码报告“RuntimeBinderException:'Foo'不包含'Text'的定义。”

我们最终完成的工作是显示编译器尝试使用您的Select方法实现LINQ Select运算符。然后,它会尝试查看是否可以使用已定义的Select方法作为所显示类型的重载,并且由于dynamic可以“强制转换”为IColumn,它会显示您的Select方法是最好的超载。此外,由于编译器只搜索当前类中的候选者,因此不会继续查找标准LINQ运算符。

当然,然后编译器使用您的重载将dynamic强制转换为object,然后尝试查找.Text属性。它当然不能,所以它会报告你的错误。

答案 3 :(得分:0)

好吧,看看the bug report已经解决了很长时间了,让我们总结一下:

这是一个错误,编译器没有按原样应用dynamic标志,导致它变为object。该错误已得到修复。我不知道VS2015何时可以使用,也许其他人可以提供这些信息。

这可能会在重载解析机制中引发一些怪癖,导致误导性错误消息和工具提示内容。