How do I specify the object to return from an expression tree method?

thecoop picture thecoop · Feb 7, 2011 · Viewed 11.1k times · Source

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:

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

Answer

Ben picture Ben · Oct 23, 2012

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.

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.