我正在向一些自定义对象添加 LINQ 接口,但C# 编译器在 type inference 上失败。但是,我可以使用原始扩展方法编写等效查询并且类型推断成功,因此我不确定编译器如何将查询表达式转换为扩展方法调用。
是否有工具或编译器标志,以便我可以查看编译器从我的查询表达式生成的内容,以便我弄清楚这一点?
此代码位于开源项目中,因此如果有帮助,我可以提供源代码链接。扩展方法的类型签名的细微变化避免了这种类型推断错误,但这些变体没有我所追求的语义。
我正在向一些自定义对象添加 LINQ 接口,但C# 编译器在 type inference 上失败。但是,我可以使用原始扩展方法编写等效查询并且类型推断成功,因此我不确定编译器如何将查询表达式转换为扩展方法调用。
是否有工具或编译器标志,以便我可以查看编译器从我的查询表达式生成的内容,以便我弄清楚这一点?
此代码位于开源项目中,因此如果有帮助,我可以提供源代码链接。扩展方法的类型签名的细微变化避免了这种类型推断错误,但这些变体没有我所追求的语义。
您的查询理解代码是:
from f1 in e1
from f2 in e2
from f3 in e3
select f3
您的方法调用代码是:
e1
.SelectMany(f1 => e2)
.SelectMany(f2 => e3), (f2, f3) => f3))
查询翻译如下进行。首先我们处理前两个 from 子句:
from f1 in e1
from f2 in e2
from f3 in e3
select f3;
这被翻译成
from x in ( e1 ) . SelectMany( f1 => e2 , ( f1 , f2 ) => new { f1 , f2 } )
from f3 in e3
select f3;
其中“x”是透明标识符。由于 e1、e2 或 e3 都不使用任何范围变量,因此这是一个透明标识符这一事实是无关紧要的;无需进一步重写来处理透明标识符语义。
然后将该结果转换为
( ( e1 ) . SelectMany( f1 => e2 , ( f1 , f2 ) => new { f1 , f2 } ) )
.SelectMany( x => e3 , ( x , f3 ) => f3 )
我们可以去掉其中的一些括号:
e1
.SelectMany( f1 => e2 , ( f1 , f2 ) => new { f1 , f2 } ) )
.SelectMany( x => e3 , ( x , f3 ) => f3 )
显然,这与您手动完成的句法转换有很大不同,回想一下,
e1
.SelectMany(f1 => e2)
.SelectMany(f2 => e3), (f2, f3) => f3))
如果您将 e1、e2、e3 替换为上面的实际句法转换,则生成的表达式是否通过类型推断?
如果没有,那么问题是“为什么不呢?” 您的代码有问题,或者类型推断器有问题。如果类型推断器有问题,请告诉我。
如果是这样,那么问题是“句法转换传递有什么问题”?如果语法转换传递有问题,请再次告诉我。
谢谢!
您可以使用Reflector并在关闭优化的情况下查看您的代码。
Eric 的概述使我了解了这些查询是如何处理的。问题是我试图以查询翻译不喜欢的方式限制正在操作的类型。
from x in Foo.Bar()
...
Foo.Bar() 应该返回一个 Future,x 也应该是 Future 类型,但这不适用于查询翻译。我通过添加另一层间接来解决这个问题,基本上将期货包装在 Async<T> 类型中,它只能用期货实例化,即。
public sealed class Async<T> { internal T value; }
public static class Async
{
public static Async<Future<T>> Begin<T>(Future<T> future) { ... }
}
然后我可以在 Async 值上编写查询计算,因此表达式变为:
from x in Async.Begin(Foo.Bar())
...
其中 x 现在是 future 类型,我可以任意强制或推迟期货和承诺。
谢谢大家的建议。不过,内置到 Visual Studio 中的查询表达式转换器会很好,以防 MS 的任何人正在阅读此内容。;-)