如何指定从表达式树方法返回的对象?

the*_*oop 21 .net expression-trees

我正在尝试使用返回对象的表达式树创建一个方法,但我无法弄清楚如何实际指定要返回的对象.我试过读过这个,但实际上似乎没有在任何地方指定返回值.

我已经完成了所有的任务和内容,但是如何指定从使用表达式树创建的方法返回的对象?

编辑:这些是v4表达式树,我试图创建的方法是这样的:

private object ReadStruct(BinaryReader reader) {
    StructType obj = new StructType();
    obj.Field1 = reader.ReadSomething();
    obj.Field2 = reader.ReadSomething();
    //...more...
    return obj;
}
Run Code Online (Sandbox Code Playgroud)

Ben*_*Ben 31

在返回现有参数或变量的情况下,有一种更简单的方法可以做到这一点.块表达式中的最后一个语句成为返回值.您可以在结尾处再次包含ParameterExpression以使其返回.

假设你的结构是这样的:

public struct StructType
{
    public byte Field1;
    public short Field2;
}
Run Code Online (Sandbox Code Playgroud)

然后你的代码看起来像这样:

var readerType = typeof(BinaryReader);
var structType = typeof(StructType);
var readerParam = Expression.Parameter(readerType);
var structVar = Expression.Variable(structType);

var expressions = new List<Expression>();

expressions.Add(
    Expression.Assign(
        Expression.MakeMemberAccess(structVar, structType.GetField("Field1")),
        Expression.Call(readerParam, readerType.GetMethod("ReadByte"))
        )
    );

expressions.Add(
    Expression.Assign(
        Expression.MakeMemberAccess(structVar, structType.GetField("Field2")),
        Expression.Call(readerParam, readerType.GetMethod("ReadInt16"))
        )
    );

expressions.Add(structVar); //This is the key. This will be the return value.

var ReadStruct = Expression.Lambda<Func<BinaryReader, StructType>>(
    Expression.Block(new[] {structVar}, expressions),
    readerParam).Compile();
Run Code Online (Sandbox Code Playgroud)

测试它的工作原理:

var stream = new MemoryStream(new byte[] {0x57, 0x46, 0x07});
var reader = new BinaryReader(stream);
var struct1 = ReadStruct(reader);
Run Code Online (Sandbox Code Playgroud)

值得一提的是,如果StructType是一个结构,这个例子是有效的.如果它是一个类,你只需调用构造函数并在BlockExpression中初始化structVar.


R. *_*des 23

显然,您可以使用工厂方法创建return一个.你需要在最后创建一个标签来跳转到它.像这样的东西:GotoExpressionExpression.Return

// an expression representing the obj variable
ParameterExpression objExpression = ...;

LabelTarget returnTarget = Expression.Label(typeof(StructType));

GotoExpression returnExpression = Expression.Return(returnTarget, 
    objExpression, typeof(StructType));

LabelExpression returnLabel = Expression.Label(returnTarget, defaultValue);

BlockExpression block = Expression.Block(
    /* ... variables, all the other lines and... */,
    returnExpression,
    returnLabel);
Run Code Online (Sandbox Code Playgroud)

标签目标的类型和goto表达式必须匹配.由于标签目标具有类型,因此标签表达式必须具有默认值.

  • @thecoop:我想这是因为它需要支持许多不同语言的语言.例如,在VB中,您可以省略return语句(Option Strict Off),这就是默认值在那里的原因.我确信其他的恶作剧还支持其他一些语言的其他特质.我当然希望至少有关于这一切的文件更多.我不得不做一些试验和错误,并查看一些DLR来源来弄清楚. (3认同)
  • @thecoop:不得不改变一些其他的东西,但我得到了它的工作.该死,这肯定是很麻烦的工作! (2认同)
  • 唉,这很有效.知道为什么它如此复杂吗? (2认同)

小智 17

我发现了一些隐含(一种情况)或实际状态(另一种情况)的来源,从表达式返回一个值可以简单地通过使用与所讨论的值对应的参数表达式来结束该块来完成,因为最后一个块中的值表达式成为其返回值.据报道,Expression.Return工厂存在于更复杂的情况,其中一个从代码块的中间返回.

换句话说,如果块中的最后一个表达式只是objExpression,那就足够了.我认为,如果使用Return方法和标签解构所有业务,实际发生的是objExpression基本上传递给标签(在块的末尾)并离开那里,就像你消除returnExpression一样和returnLabel并简单地用objExpression结束.

不幸的是,我自己无法真正测试这个.

  • 在.NET 4.5中验证 (5认同)