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

时间:2022-08-08 21:32:18

I'm trying to create a method using an expression tree that returns an object, but I can't figure out how to actually specify the object to return. I've tried reading this, but the return value doesn't actually seem to be specified anywhere.

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

I've got all the assignments & stuff down, but how do I specify the object to return from a method created using expression trees?

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

EDIT: these are v4 expression trees, and the method I'm trying to create does something like this:

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

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

3 个解决方案

#1


18  

Apparently a return is a GotoExpression that you can create with the Expression.Return factory method. You need to create a label at the end to jump to it. Something like this:

显然,返回是您可以使用Expression.Return工厂方法创建的GotoExpression。您需要在末尾创建一个标签才能跳转到它。像这样的东西:

// 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);

The types of the label target and the goto expression must match. Because the label target has a type, the label expression must have a default value.

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

#2


24  

There is a much easier way to do this in cases that return an existing parameter or variable. The last statement in a Block Expression becomes the return value. You can include the ParameterExpression again at the end to have it returned.

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

Assuming your struct is like this:

假设你的结构是这样的:

public struct StructType
{
    public byte Field1;
    public short Field2;
}

Then your code would look like this:

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

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();

Test that it works:

测试它的工作原理:

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

It's worth mentioning this example works if StructType is a struct. If it is a class, you just call the constructor and initialize structVar first thing in the BlockExpression.

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

#3


11  

I have found a couple of sources that imply (one case) or actually state (another case) that returning a value from an expression can simply be done by concluding the block with the parameter expression that corresponds to the value in question, since the last valued expression from a block becomes its return value. Reportedly, the Expression.Return factory exists for more complicated cases where one returns from the middle of a block of code.

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

In other words, if the last expression inside your block were simply objExpression, that should be sufficient. I think, if you deconstruct all that business with the Return method and the label, that what actually happens is the objExpression is basically delivered to the label (at the end of the block) and left there, much as would occur if you eliminated returnExpression and returnLabel and simply concluded with objExpression.

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

Unfortunately, I am not in a position actually to test this myself.

不幸的是,我实际上无法自己测试这个。

#1


18  

Apparently a return is a GotoExpression that you can create with the Expression.Return factory method. You need to create a label at the end to jump to it. Something like this:

显然,返回是您可以使用Expression.Return工厂方法创建的GotoExpression。您需要在末尾创建一个标签才能跳转到它。像这样的东西:

// 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);

The types of the label target and the goto expression must match. Because the label target has a type, the label expression must have a default value.

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

#2


24  

There is a much easier way to do this in cases that return an existing parameter or variable. The last statement in a Block Expression becomes the return value. You can include the ParameterExpression again at the end to have it returned.

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

Assuming your struct is like this:

假设你的结构是这样的:

public struct StructType
{
    public byte Field1;
    public short Field2;
}

Then your code would look like this:

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

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();

Test that it works:

测试它的工作原理:

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

It's worth mentioning this example works if StructType is a struct. If it is a class, you just call the constructor and initialize structVar first thing in the BlockExpression.

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

#3


11  

I have found a couple of sources that imply (one case) or actually state (another case) that returning a value from an expression can simply be done by concluding the block with the parameter expression that corresponds to the value in question, since the last valued expression from a block becomes its return value. Reportedly, the Expression.Return factory exists for more complicated cases where one returns from the middle of a block of code.

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

In other words, if the last expression inside your block were simply objExpression, that should be sufficient. I think, if you deconstruct all that business with the Return method and the label, that what actually happens is the objExpression is basically delivered to the label (at the end of the block) and left there, much as would occur if you eliminated returnExpression and returnLabel and simply concluded with objExpression.

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

Unfortunately, I am not in a position actually to test this myself.

不幸的是,我实际上无法自己测试这个。