IEnumerable<dynamic> 上的 LINQ Select 编译时错误
Compile-time error with LINQ Select on IEnumerable<dynamic>
重大更新请往下看!
我有一些这样的代码:
void Test(IEnumerable x)
{
var dynX = x.Cast<dynamic>();
var result = dynX.Select(_ => _.Text);
}
在针对 .NET 4.5 的现有库项目中。 VS2015 的 IntelliSense 强调 Text
部分,抱怨:'object' 不包含 'Text'...
的定义
果然,编译失败
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?)
此消息总是显示 '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/the 新编译器版本解析扩展方法的方式存在严重错误。
以下只是松散相关,主要是关于误导性错误消息。我决定保留它以免混淆评论。
更糟糕的是,即使 IEnumerable
和 object
都不可能有扩展方法 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 上进行跟踪。
你可以用这个
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);
}
不能说为什么它在一个 VS 而不是另一个 VS 中工作,但这就是我会做的
重命名。
public static IColumn Select<TResult>(this IColumn source, Func<object, TResult> selector)
{
throw new NotImplementedException();
}
"Select" 是其他公共库的方法名称的构建。
所以我强烈建议您将其重命名为 "AppColumnSelectText" 之类的名称或其他名称,但不是 "Select".
然后改变
public interface IColumn { }
到
public interface IColumn
{
string Text {get; set;}
}
然后实施它
public class MyClass : IColumn
{
public string Text { get; set;}
}
然后将您的动态转换为 IColumn,假设它来自 MyClass class 类型
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);
}
}
更新
除此之外...请参见下面的插图 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();
}
}
您可以使用您的 Select...但它只适用于实现 IColumn 的东西,这就是为什么我发现很难看出它如何工作的原因。
这只是一个案例,其中 c# 使用鸭子类型的形式来允许 LINQ 适用于任何类型。
我将从一个简单的例子开始。
如果我定义 class 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;
我得到一个适用于 Foo
(不是 IEnumerable<T>
)类型的 LINQ 版本,returns 和 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' does not contain a definition for 'Text'"。
我们最后所做的是显示编译器尝试使用您的 Select
方法实现 LINQ Select
运算符。然后它会尝试查看它是否可以使用定义的 Select
方法作为所呈现类型的重载,并且由于 dynamic
可以是 "cast" 到 IColumn
它说你的 Select
方法是最好的重载。此外,由于编译器只会搜索当前 class 中的候选项,因此它不会继续查找标准的 LINQ 运算符。
当然,然后编译器使用您的重载,将 dynamic
强制转换为 object
,然后尝试找到 .Text
属性。它当然不能,所以它会报告你的错误。
嗯,看到the bug report解决了好久,总结一下:
这是一个错误,编译器没有按应有的方式应用 dynamic
标志,导致它变成 object
。该错误已修复。不知道什么时候能在VS2015中使用,也许其他人可以提供那个信息。
这可能触发了重载解析机制中的一些问题,导致误导性的错误消息和工具提示内容。
重大更新请往下看!
我有一些这样的代码:
void Test(IEnumerable x)
{
var dynX = x.Cast<dynamic>();
var result = dynX.Select(_ => _.Text);
}
在针对 .NET 4.5 的现有库项目中。 VS2015 的 IntelliSense 强调 Text
部分,抱怨:'object' 不包含 'Text'...
果然,编译失败
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?)
此消息总是显示 '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/the 新编译器版本解析扩展方法的方式存在严重错误。
以下只是松散相关,主要是关于误导性错误消息。我决定保留它以免混淆评论。
更糟糕的是,即使 IEnumerable
和 object
都不可能有扩展方法 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 上进行跟踪。
你可以用这个
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);
}
不能说为什么它在一个 VS 而不是另一个 VS 中工作,但这就是我会做的
重命名。
public static IColumn Select<TResult>(this IColumn source, Func<object, TResult> selector)
{
throw new NotImplementedException();
}
"Select" 是其他公共库的方法名称的构建。 所以我强烈建议您将其重命名为 "AppColumnSelectText" 之类的名称或其他名称,但不是 "Select".
然后改变
public interface IColumn { }
到
public interface IColumn
{
string Text {get; set;}
}
然后实施它
public class MyClass : IColumn
{
public string Text { get; set;}
}
然后将您的动态转换为 IColumn,假设它来自 MyClass class 类型
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);
}
}
更新 除此之外...请参见下面的插图 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();
}
}
您可以使用您的 Select...但它只适用于实现 IColumn 的东西,这就是为什么我发现很难看出它如何工作的原因。
这只是一个案例,其中 c# 使用鸭子类型的形式来允许 LINQ 适用于任何类型。
我将从一个简单的例子开始。
如果我定义 class 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;
我得到一个适用于 Foo
(不是 IEnumerable<T>
)类型的 LINQ 版本,returns 和 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' does not contain a definition for 'Text'"。
我们最后所做的是显示编译器尝试使用您的 Select
方法实现 LINQ Select
运算符。然后它会尝试查看它是否可以使用定义的 Select
方法作为所呈现类型的重载,并且由于 dynamic
可以是 "cast" 到 IColumn
它说你的 Select
方法是最好的重载。此外,由于编译器只会搜索当前 class 中的候选项,因此它不会继续查找标准的 LINQ 运算符。
当然,然后编译器使用您的重载,将 dynamic
强制转换为 object
,然后尝试找到 .Text
属性。它当然不能,所以它会报告你的错误。
嗯,看到the bug report解决了好久,总结一下:
这是一个错误,编译器没有按应有的方式应用 dynamic
标志,导致它变成 object
。该错误已修复。不知道什么时候能在VS2015中使用,也许其他人可以提供那个信息。
这可能触发了重载解析机制中的一些问题,导致误导性的错误消息和工具提示内容。