Lambda表达式`x => x.Property`变为`x =>转换(x.Property)`

时间:2015-10-13 10:44:33

标签: c# object lambda

我有一个示例Data

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"; }
}

和扩展方法

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

我像这样使用

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

我的Method<T>确实收到了3个属性,但它已略微更改为:

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

如您所见,TestString部分保持不变。我尝试将我的属性更改为params Expression<Func<T, bool>>[]params Expression<Func<T, int>>[],并且只传递相应的参数,它可以正常工作。我理解问题来自转换为object,但我无法弄清楚。

3 个答案:

答案 0 :(得分:4)

由于Int32Boolean都不是引用类型,因此整个表达式树需要将它们显式地转换为object

在编译时使用常规C#编译器可以使用一些隐式操作,而其他操作在实现表达式树时需要显式操作。

你想测试自己这个事实吗?

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

在一天结束时,常规C#编译器会实现一些 trickery 来转换值类型以适合object(引用类型)。也许这个Q&amp; A“How do ValueTypes derive from Object (ReferenceType) and still be ValueTypes?”Eric Lippert回答它可能对你很有意思。

OP说...

  

是否有任何方法可以强制表达保持不受影响?

没有。您应该处理这两种情况:使用和不使用强制转换来访问属性。

答案 1 :(得分:3)

如果要分析原始表达式,一种可能的方法是手动删除Convert表达式。

Method中,您可能会获得UnaryExpression with NodeType = Convert。如果是这样,只需检查此表达式的Operand属性。

答案 2 :(得分:1)

我不确定你想要实现什么,但这是一种不进行这些转换的方法。问题是您正在转换为object,这只会因为您声明Expression变量/参数的方式而发生。

当你说:

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

这不会转换。所以这样做:

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

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

Method(myFunctionExpressions);

myFunctionExpressions的论点也必须是LambdaExpression[]

呼叫者现在变得更加冗长,但树木很干净。