1

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

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

此代码位于开源项目中,因此如果有帮助,我可以提供源代码链接。扩展方法的类型签名的细微变化避免了这种类型推断错误,但这些变体没有我所追求的语义。

4

3 回答 3

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 替换为上面的实际句法转换,则生成的表达式是否通过类型推断?

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

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

谢谢!

于 2009-12-07T16:56:00.823 回答
1

您可以使用Reflector并在关闭优化的情况下查看您的代码。

于 2009-12-07T01:05:48.160 回答
0

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 的任何人正在阅读此内容。;-)

于 2009-12-10T03:09:54.350 回答