2009-12-07 188 views
1

我正在给一些自定义对象添加LINQ接口,但是C# compiler fails on type inference。但是,我可以使用原始扩展方法编写等效查询,并且类型推导成功,所以我不确定编译器如何将查询表达式转换为扩展方法调用。LINQ查询表达式翻译器?

是否有工具或编译器标志,以便我可以查看编译器从我的查询表达式中生成的内容,所以我知道了这一点?

此代码位于开源项目中,因此如果有帮助,我可以提供指向源代码的链接。扩展方法的类型签名有轻微的变化,以避免这种类型的推理错误,但是这些变体没有我之后的语义。

+2

编译器用于翻译查询表达式的精确系列步骤是在C#规范中,您可以从Internet获得该规范。 – 2009-12-07 16:34:13

回答

4

您查询综合代码为:

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替换为实际值上面的语法转换,得到的表达式传递类型推断?

如果没有,那么问题是“为什么不?”你的代码有问题,或者类型推理器有问题。如果类型推理者出现问题,请告诉我。

如果是这样,那么问题是“句法转换过程有什么问题”?如果语法转换过程有问题,请再告诉我。

谢谢!

+0

顺便说一句,f1和f2不在范围内,因为匿名类型现在封装在将来。 – naasking 2009-12-07 19:32:14

+0

通过对扩展方法进行小调整来使用翻译,可以在返回f3时正确编译查询,但f1和f2不在范围内。问题在于我在未来的类型上运行,我只想从客户端代码中提取未来的价值。所以,通常的SelectMany的样子:未来的SelectMany (这个未来 FUT,Func键> SEL,Func键 RES),但我需要它看起来像:未来的SelectMany (这个未来 FUT,Func键,未来> SEL ,Func ,Future ,Future > res)想法? – naasking 2009-12-07 19:34:28

+0

事实上,以下就足够了: 未来的SelectMany (这个未来 FUT,Func键,未来> SEL,Func键,未来,R> RES) 这也不起作用。 – naasking 2009-12-07 19:35:34

1

您可以使用Reflector并关闭优化来查看您的代码。

+0

不,如我所说查询表达式的代码不能编译,所以我没有什么要检查的。我正在寻找一些工具/技术来将查询表达式的源代码翻译成使用等效扩展方法的源代码,以便我可以看到为什么查询表达式不会编译。 – naasking 2009-12-07 04:11:20

+0

你可以发布代码吗?也许如果我看看它,我可以帮助你弄清楚发生了什么。 – 2009-12-07 04:12:17

+0

具有扩展方法的核心类,在427行及以后定义的SelectMany: http://sasa.svn.sourceforge.net/viewvc/sasa/trunk/Sasa/Futures.cs?revision=416&view=markup 测试,请参阅行93方法“testLinqSuccess”为我正在描述的错误: http://sasa.svn.sourceforge.net/viewvc/sasa/trunk/Build/Tests/FutureTests.cs?revision=416&view=markup 有点更多背景:这些是使用期货的异步计算。我的第一个版本的行为就像Error monad,如果以前的异常失败,所有后续的期货都会失败,但这不是所需的语义。我很欣赏任何见解。 – naasking 2009-12-07 04:31:35

0

Eric的概述让我明白了如何处理这些查询。问题在于我试图以查询翻译不喜欢的方式限制正在操作的类型。

from x in Foo.Bar() 
... 

Foo。Bar()应该返回一个Future,x也应该是Future类型的,但这不适用于查询翻译。我通过增加另一层间接寻址来解决这个问题,基本上是将期货包装在一个异步类型中,这种异步类型只能用期货实例化,即。

public sealed class Async<T> { internal T value; } 
public static class Async 
{ 
    public static Async<Future<T>> Begin<T>(Future<T> future) { ... } 
} 

然后,我可以写上异步值查询计算,所以表达式变成一样的东西:

from x in Async.Begin(Foo.Bar()) 
... 

其中x是现在未来型的,我可以强制或推迟期货和随意承诺。

感谢大家的建议。查询表达式转换器内置到Visual Studio将是很好,但万一有人在MS阅读此。 ;-)