从Expresson Tree设置值 [英] Setting value from Expresson Tree

查看:85
本文介绍了从Expresson Tree设置值的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

看来我需要将表达式树传递给方法才能获得其名称,但是我也想设置值,有没有办法做到这一点:

It appears that I need to pass an Expression Tree to a method to be able to get its name, but i also want to set the value also, is there a way to do that:

name = Method(() => property, value);

public string Method<T>(Expression<Func<T>> property, T value)
{
  var propertyName = GetPropertyString(property);
  // Here I want to set the property to the value
  //i.e., property = value;
  return propertyName;
}



谢谢

注意:使用反射的解决方案将很好地工作,除了该方法在基类中并且属性在派生类中之外,因此必须将引用传递给派生类才能使解决方案起作用.我希望可以通过某种方式使用属性信息来设置值.在表达式树"信息中发送属性引用同样干净.有人足够了解Expresson树吗?



Thanks

Note: The solutions using reflection would work well, except that the method is in the base class and the properties are in a derived class, so that would have to pass the a reference to the derived class in order for solution to work. I was hopeing there was some way to use the property information to set the value. It would be just as clean to send the property reference in the Expression Tree information. Anybody understand expresson trees well enough?

推荐答案

如果您愿意为可维护性论点付出永久的运行时惩罚,则可以通过反思来做到.您还将丢失类型的savety.

If you are willing to pay a permanent runtime penalty for your argument of maintainability, you could do it via reflection. You would also lose type savety.

private static void SetStatic<T>(string prop, T value)
{
    var p = MethodInfo.GetCurrentMethod().DeclaringType.GetProperty(prop);
    p.SetValue(null, value, null);
}
private void Set<T>(string prop, T value)
{
    var p = MethodInfo.GetCurrentMethod().DeclaringType.GetProperty(prop);
    p.SetValue(this, value, null);
}

public static int StaticProp { get; set; }
public int Prop { get; set; }

static void Main(string[] args)
{
    SetStatic("StaticProp", 7);
    var obj = new Program();
    obj.Set("Prop", 123);
}



我怀疑这是否是合理的情况-属性的更改频率不足以证明这种开销是合理的.

就是我的5美分.

干杯

Andi



I doubt if this is a reasonable scenario - properties do not change often enough to justify this overhead.

Just my 5 cents.

Cheers

Andi


或解决方案1的替代方案;;))
Or an alternative of Solution #1 to play around a bit ;-)

public static int StaticProp { get; set; }
public int Prop { get; set; }

private static string StaticSet<T>(Expression<Func<T>> p, T v)
{
    string name = p.Body.ToString().Split('.', '+', '/').Last();
    MethodInfo.GetCurrentMethod().DeclaringType.GetProperty(name).SetValue(null, v, null);
    return name;
}
private string Set<T>(Expression<Func<T>> p, T v)
{
    string name = p.Body.ToString().Split('.', '+', '/').Last();
    MethodInfo.GetCurrentMethod().DeclaringType.GetProperty(name).SetValue(this, v, null);
    return name;
}

static void Main(string[] args)
{
    string name = StaticSet(() => StaticProp, 17);

    var obj = new Program();
    name = obj.Set(() => obj.Prop, 53);

    Console.WriteLine("{0} = {1}", name, StaticProp);
    Console.WriteLine("{0} = {1}", name, obj.Prop);
}



输出:



Output:

StaticProp = 17
Prop = 53



这里的窍门是拥有属性的委托(必须具有可访问的getter!),以一种类型安全的方式将属性名称的信息传输到Set ...()方法.但是,永远不会执行委托.您仍然可以通过提供任何匹配的委托来滥用,这会使事情搞砸.

尽管如此:我认为这是不值得的,永远也不要.请参阅我的评论以及解决方案#1.

干杯

Andi



The trick here is to have a delgate of the property (that must have an accessible getter!) as means to transport the information of the property name in a kind of typesafe way to the Set...() method. The delecate is never executed, though. You still can abuse by providing any matching delegate, which screws up the thing.

Still: I think it''s not worth the pain, never. See my comments as well as solution #1.

Cheers

Andi


Hello Clifford,

如果我先详细介绍一下lambda表达式,然后解释为什么您不可能使用 properties 做些什么,也许会有所帮助.

委托:它们的作用



委托是C#在不执行方法的情况下持有方法句柄的方法.一旦有了该句柄,就可以随时执行该句柄指向的方法.

这是延迟执行.这是代表的主要目的.

定义委托的方式



传统方式



传统方式是定义委托类型,并在需要递延执行功能的任何地方使用它,例如

Hello Clifford,

maybe it helps if I first elaborate a bit on lambda expressions and then explain why it is not possible what you aim to do with properties.

Delegate: what they are for



A delegate is the means of C# to hold a handle to a method without executing the method. Once you have that handle, you can execue the method the handle points to at any time.

This is deferred execution. This is the primary purpose of delegates.

Ways to define delegates



The traditional way



The traditional way is to define a delegate type and use it wherever you need that deferred execution capability, e.g.

// define your own delegate type
public delegate void PrintStringDelegate(string arg);





// your print method
public static void ToConsole(string s)
{
    Console.WriteLine("{0}", s);
}





// define a variable that can hold a delegate
PrintStringDelegate print;

// "plain old" C# ...
print = new PrintStringDelegate(ToConsole);

// ...or more convenient abbreviation...
print = ToConsole;

// ... or anonymous function (a nameless method that takes a string argument)
print = delegate(string s) { Console.WriteLine("{0}", s); };

// ... and finally execute it where appropriate:
print("Hello deferred world!");



变得有点花哨:Action,Action< ...>和Func< ...>



您可以使用已经预定义的通用等效项来代替定义自己的委托类型.在上述情况下,我们有一个采取一个字符串参数的操作(一个函数将返回一个值,但是上面的示例没有,因此,Func不是正确的泛型委托).

例如.与上述相同,但具有操作:



Become a bit fancy: Action, Action<...>, and Func<...>



Instead of defining your own delegate type, you may use the already predefined generic equivalents. In the above case we have an action that takes one string argument (a function would return a value, but the example above doesn''t, so, the Func is not the right generic delegate).

E.g. the same as above, but with Action:

// no need for your own delegate type: take Action instead direclty for the variable:
Action<string> print;

// "plain old" C# ...
print = new Action<string>(ToConsole);

// ...or more convenient abbreviation...
print = ToConsole;

// ... or anonymous function (a nameless method that takes a string argument)
print = delegate(string s) { Console.WriteLine("{0}", s); };

// ... and finally execute it where appropriate:
print("Hello deferred world!");



顺便说一句:为了方便起见,还定义了此类泛型委托,例如Predicate<in T>,与Func<in T, bool>相同,再次与public delegate bool MyPredicateDelegate<in T>(T arg);相同.

更花哨的是:lambda表达式



如上所述,我们可以简化"我的Action和Func来代替我们自己的委托类型定义.

我们还可以定义委托指向的方法,即在定义委托变量的位置.匿名方法(例如
)已经可以做到这一点



BTW: there are further such generic delegates defined for convenience, e.g. Predicate<in T>, which is identical to Func<in T, bool>, and again identical to public delegate bool MyPredicateDelegate<in T>(T arg);.

Even more fancy: lambda expressions



As we''ve seen above, we can "simplify" my taking Action and Func in place of our own delegate type definition.

We can also define the method the delegate points inplace, i.e. at the point where we define the delegate variable. This was already possible with the anonymous methods, e.g.

// anonymous function (a nameless method that takes a string argument)
print = delegate(string s) { Console.WriteLine("{0}", s); };



Lambda表达式可以进一步简化,例如,



Lambda expression allow to simplify that further, e.g.,

// lambda syntax to write an anonymous function
print = (s) => { Console.WriteLine("{0}", s); };



Lambda表达式是匿名方法,其作用与通过delegate关键字定义的匿名方法相同,如上所示.

甚至lambda表达式都可以具有任何复杂性,它们对于小型内联表达式最方便,因此,可能有一些abbreviatons,例如



Lambda expressions are anonymous methods, identical in effect to the anonymous methods defined via the delegate keyword as shown above.

Even lambda expressions can be of any complexity, they are most handy for small inline expressions, therefore, there are some abbreviatons possible, e.g.

// one arg = no parenthesis needed
print = s => { Console.WriteLine("{0}", s); };

// single statement in the block = curly braces not needed
print = s => Console.WriteLine("{0}", s);



lambda表达式的一个优点是您可以使用其直接环境中的变量(与方法不同),例如



A nicety of lambda expresions is that you can use variables from its immediate environment (unlike methods), e.g.

string tag = "";
print = s => Console.WriteLine("{0}: {1}", tag, s);

tag= "Error";
print("some");
tag= "Warning";
print("more text");



输出为:



The output is:

Error: some text
Warning: more text



记住:这里我们仍然是延迟执行的委托.

代理人和表达方式

表达式...与其他类不同,它不是一个类:它是编译器比任何普通类都了解更多的类.表达式...类表示编译器看到的lambda表达式.

表达式...的目的是为了...该类允许在运行时通过C#代码分析lambda表达式.这是允许为Linq实现任何类型的数据提供程序所必需的.

C#语言要求类型为Expression的变量...只能包含文字lambda表达式.不要与分配可能已设置为lambda表达式的委托相混淆.该赋值在C#编译器编译时进行评估(您要记住:与其他常见类相比,它是编译器更了解的类).

例如.可以将lambda表达式分配给Expression< ...>类型的变量:



Remember: what we have here are still delegates for deferred execution.

Delegates and Expression<...>



Expression<...> is not a class like any other class: it is a class that the compiler knows more about than on any common class. The Expression<...> class represents a lambda expression as the compiler sees it.

The aim of the Expression<...> class is to allow analyzing the lambda expression by C# code at runtime. This was needed to allow implementing any kind of data provider for Linq.

The C# language mandates that a variable of type Expression<...> can only hold a literal lambda expression. Not to be confused with assigning a delegate that might have been set to a lambda expression. The assignment is evaluated at compile time by the C# compiler (you remember: it is a class the compiler knows more about than of other common classes).

E.g. one can assign a lambda expression to a variable of type Expression<...>:

Expression<Action<string>> e = s => Console.WriteLine("{0}", s);



但是不能将委托分配给该变量,例如这将无法编译:



But one can not assign a delegate to that varialbe, e.g. this will not compile:

Action<string> print = s => Console.WriteLine("{0}", s);
Expression<Action<string>> e = print; // see error below...



错误为:



The error is:

Cannot implicitly convert type ''System.Action<string>'' to ''System.Linq.Expressions.Expression<System.Action<string>>''



您将不得不再次传递lambda表达式:



You would have to pass a lambda expression again:

Action<string> print = s => Console.WriteLine("{0}", s);
Expression<Action<string>> e = s=>print(s);



委托的执行与Expression的执行



通过使用所需参数(例如
)调用"委托来执行委托



Execution of delegate versus execution of Expression



A delegate is executed by "calling" it with the needed parameters, e.g.

print = s => { Console.WriteLine("{0}", s); };
print("Hello...");



在执行之前必须先编译表达式(如果需要的话,但是通常这不是使用Expression< ...>类的主要目的):



An expression has to be compiled before executed (if that was needed, but usually this is not the primary purpose of the using the Expression<...> class):

Expression<Action<string>> e = s => Console.WriteLine("{0}", s);
Action<string> action = e.Compile();
action("Hello...");



顺便说一句:如果执行lambda表达式是主要目的,那么委托人将这样做.如果分析lambda表达式的结构,则表达式...类是选择的工具".

属性和lambda表达式



属性在.Net框架中起着主导作用.最好在序列化和所有声明性接口描述(WCF,WPF等)中使用它们.

因此,可能很想构造一些魔术"包装函数以传递对属性的引用"并对其进行一些分配并触发一些事件,等等.

由于Lambda表达式的主体就像一个函数主体(也许带有一些语法糖来简化它们的编写),因此以下形式的Lambda表达式将调用该属性的 getter ( 不是对该属性的引用.



BTW: If execution of the lambda expression is the main purpose then delegates will do it. If analyzing the structure of the lambda expression, the Expression<...> class is the "tool of choice".

Properties and lambda expressions



Properties have a dominant role in the .Net framework. They are preferrably used in serialization and in all the declarative interface descriptions (WCF, WPF, etc.).

So, one might be tempted to construct some "magic" wrapper functions to pass "a reference to a property" and do some assignment to it and trigger some events, etc.

Since a lambda expression''s body is like a function body (maybe with some syntactic sugar to abbreviate the writing of them), a lambda expression of the following form is calling the getter of the property (This is not a reference to the property.

Func<int> func = ()=>obj.Prop;



C#中无法在任何地方传递属性的引用.

现在,为什么不传递Expression< ...> ;?实例的魔术"方法?例如



There is no way in C# to pass a reference of a property anywhere.

Now, why not passing an Expression<...> instance to the "magic" method? E.g.

public static void MagicFunction(Expression<Func<int>> prop)
{
   // aim:
   // 1. get the name of the property for some magic action
   // 2. assign a value to the propery
}

MagicFunction(()=>obj.Prop);



获取属性的名称是可能的.这取决于您为使其变得强大而付出的努力(即避免说MagicFunction(()=>12);滥用魔术功能).

现在,也可以分配属性,但这是一种语义上的废话":您通过吸气剂来设置属性.

方法是从给定的类型或实例中按名称获取属性,并在其上调用SetValue(...).



Getting the name of the property is possible. It depends on the effort you put into making this robust (i.e. avoid abuse of the magic function with say MagicFunction(()=>12);).

Now, assigning to the property is also possible, but this is a semantic "non-sense": you pass a getter to set the property.

The way was to get the propery by name from the given type or instance and call SetValue(...) on that.

// static or instance properties
public static void Set<C, V>(C obj, Expression<Func<V>> p, V v)
{
    string name = p.Body.ToString().Split('.', '+', '/').Last(); // not robust...
    typeof(C).GetProperty(name).SetValue(obj, v, null);
}
// static properties
public static void Set<C, V>(Expression<Func<V>> p, V v)
{
    Set<C, V>(default(C), p, v);
}



用法:



Usage:

Set<program,>(()=>StaticProp, 123);
Program obj = new Program();
Set(obj, ()=>obj.Prop, 234); // note: the generic args are deduced



摘要



在我对代理人,lambda表达式以及最后的Expression ...进行了长时间的介绍和阐述之后,类,简单的结论是您可以做一些魔术的事情(希望这不再是魔术了).但是,仅仅因为您可以可以做,并不意味着您应该可以做.

我认为滥用属性的 Expression< ...> getter 对属性进行赋值也是语义完整性方面的很大努力.

现在您有了背景,您可能会同意;-)

干杯

安迪

PS:现在我几乎在文章的范围和形式上都写了一个解决方案... ;-)玩得开心!



Summary



After my lengthly introduction and elaboration on delegates, lambda expressions and finally the Expression<...> class, the simple conclusion is that you can do some magic stuff (which is hopefully not so magic any more). But just since you could do it does not mean you should do it.

I consider abusing the Expression<...> and the getter of a property to do an assignment on the property is too much of a stretch for semantic integrity.

Now that you got the background you might agree ;-)

Cheers

Andi

PS: Now I ended up writing a solution almost in extent and form of an article... ;-) Have fun!


这篇关于从Expresson Tree设置值的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆