Groovy c# 表达式 eqivalent 来评估属性



在.NET中,我们可以编写如下代码来返回属性Name

public static string GetName(Expression<Func<object>> exp)
{
    MemberExpression body = exp.Body as MemberExpression;
    if (body == null) {
        UnaryExpression ubody = (UnaryExpression)exp.Body;
        body = ubody.Operand as MemberExpression;
    }
     return body.Member.Name;
 }
 public class Test
 {
     public string prop1 { get; set; }
 }

在属性内部,我们通常使用 OnPropertyChanged(() => this.prop1) 它将返回属性名称。有关详细信息,请参阅此帖子(如何在不使用字符串名称的情况下引发 PropertyChanged 事件)

我想在时髦中做一些模拟的东西,但我不确定这样做的正确方法。

    class TestObj
    {
        def prop1 = "value" 
        def prop2 = "value"
    }
    class Test{
        def something(){
            def t1 = new TestObj()
            def propertyName1 = getName{ t1.prop1 }
            def propertyName2 = getName{ t1.prop2 }
            assert propertyName1 == "prop1"
            assert propertyName2 == "prop2"
        }
    }

您将如何使用如上所示的表达式在 groovy 中实现 getName 方法

我能最接近提供这种能力对我来说感觉完全是做作的、笨拙的和临时的。它依赖于Java Bean API,由Groovy @Vetoable注解促进,以保留类代码之外的大部分丑陋:

import groovy.beans.Vetoable
import java.beans.PropertyChangeEvent
import java.beans.PropertyVetoException
class TestObj {
    @Vetoable def prop1 = "value"
    @Vetoable def prop2 = "value"
}
def t1 = new TestObj()
def getName(def t, Closure c) {
    def name
    t.vetoableChange = { PropertyChangeEvent pce ->
        if (!pce.newValue) { throw new PropertyVetoException("", pce) }
    }
    try { c.call() }
    catch (PropertyVetoException e) { name = e.propertyChangeEvent.propertyName }
    t.vetoableChange = { it -> }
    name
}
def propertyName1 = getName(t1) { t1.prop1 = null }
def propertyName2 = getName(t1) { t1.prop2 = null }
assert propertyName1 == "prop1"
assert propertyName2 == "prop2"

育!

对我来说,这种方法最有益的用途是在编译时,这样这种代码就会尽早中断,并且简单的字符串拼写错误不会在运行时导致错误。 在像groovy这样的高度动态的语言中,几乎没有办法获得适当的方法引用。 所以这里有一些代码,它在运行时执行此操作,但你唯一得到的就是一个断言(在下面的代码中),你正在控制。(clazz.&something不是真正的方法引用,例如 Clazz::getSomething在java8或C ++中)。 因此,这里有一些笨拙的尝试来滥用MethodClosure并进行微不足道的检查,如果拥有类在运行时对此有getter。

class Clazz {
    String username
    String firstName
    def lastName
    String getFake() { return 'fake' }
    void setSomething(String something) {}
}
String getName(org.codehaus.groovy.runtime.MethodClosure it) { 
    def m = it.method.toString()
    assert it.delegate.respondsTo("get${m[0].toUpperCase()}${m[1..-1]}")
    it.method.toString()
}
def c = new Clazz()
assert getName(c.&username) == 'username'
assert getName(c.&lastName) == 'lastName'
assert getName(c.&fake) == 'fake'

也就是说,我在代码示例中看到的,在我看来,这整个技术可以帮助开发人员触发事件,以防止错误键入字符串的错误。 因此,通过使用 @Bindable 来解决这个问题,可能会有一个更时髦的解决方案,它将您的属性或类的所有属性转换为"根据 JavaBeans 规范的绑定属性"。 这将为您生成整个属性更改样板代码,从而减少需要担心的代码。