Arthur Rey
Arthur Rey

Reputation: 3058

Lambda Expression `x => x.Property` gets changed to `x => Convert(x.Property)`

I have a sample Data class

public class Data
{
    public int TestInt { get; set; }
    public bool TestBool { get; set; }
    public string TestString { get; set; }

    public Data() { TestInt = 10; TestBool = true; TestString = "test"; }
}

And an extension method

public static void Method<T>(this T item, params Expression<Func<T, object>>[] properties)
{
    /* Some stuff */   
}

That I use like this

Data data = new Data();
data.Method(x => x.TestInt, x => x.TestBool, x => x.TestString);

My Method<T> does receive 3 properties, but it has been slightly changed to:

properties[0] = x => Convert(x.TestId);
properties[1] = x => Convert(x.TestBool);
properties[2] = x => x.TestString;

As you can see, the TestString part is unchanged. I tried changing my properties to params Expression<Func<T, bool>>[] and params Expression<Func<T, int>>[] and only pass the corresponding parameter and it works fine. I understand the problem comes from converting into an object but I can't figure it out.

Upvotes: 8

Views: 1996

Answers (3)

Mat&#237;as Fidemraizer
Mat&#237;as Fidemraizer

Reputation: 64931

Since both Int32 and Boolean aren't reference types, the whole expression tree needs to explicitly cast them to object.

There're some implicit operations that are available during compile-time using regular C# compiler while others require explicit actions while implementing expression trees.

Do you want to test yourself about this fact?

public struct A {}
public class B { }

public class C
{
     public A A { get; set; }
     public B B { get; set; }
}

C c = new C();
Expression<Func<C, object>> expr1 = some => some.A; // Convert(some.A)
Expression<Func<C, object>> expr2 = some => some.B; // some.B

At the end of the day, regular C# compiler implements some trickery to cast a value type to fit into object (a reference type). Maybe this Q&A "How do ValueTypes derive from Object (ReferenceType) and still be ValueTypes?" where Eric Lippert answered it might be interesting for you.

OP said...

Isn't there any way to force the Expression to remain untouched?

No. You should handle both scenarios: accessing properties with and without casts.

Upvotes: 5

qxg
qxg

Reputation: 7036

If you want to analyze original expression, one possible way is to remove Convert expression manually.

In Method, you may get a UnaryExpression with NodeType = Convert. If so, just inspect this expression's Operand property.

Upvotes: 3

usr
usr

Reputation: 171188

I'm not sure what you want to achieve but here's a way not to have these conversions. The problem is that you are converting to object which only happens because of the way you have declared the Expression variable/argument.

When you say:

Expression<Func<int>> f1 = () => 1234;

This does not convert. So do this:

Expression<Func<int>> f1 = () => 1234;
Expression<Func<string>> f2 = () => "x";

LambdaExpression[] myFunctionExpressions = new LambdaExpression[] { f1, f2 };

Method(myFunctionExpressions);

And the argument to myFunctionExpressions must be LambdaExpression[] as well.

The caller has become more verbose now but the trees are clean.

Upvotes: 1

Related Questions