【问题标题】:Groovy c# expression eqivalent to evaluate propertyGroovy c# 表达式等效于评估属性
【发布时间】:2015-01-12 11:51:07
【问题描述】:

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

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) 来返回属性名称。欲了解更多信息,请参阅这篇文章(How to raise PropertyChanged event without using string name)

我想在 groovy 中做一些类似的事情,但我不确定这样做的正确方法。

    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 方法

【问题讨论】:

  • 这是拦截器吗?

标签: groovy


【解决方案1】:

对我来说,这种方法最有益的用途是在编译时,这样这种代码会尽早中断,简单的字符串错字不会在运行时导致错误。在像 groovy 这样的高度动态的语言中,几乎没有办法获得正确的方法引用来使用。所以这里有一些代码,在运行时执行此操作,但您从中得到的唯一东西是您正在控制的断言(在下面的代码中)。 (clazz.&amp;something 不是一个真正的方法引用,就像你在 java8 或 c++ 中使用 Clazz::getSomething 那样)。所以这里有一些笨拙的尝试来滥用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 规范绑定的属性”。这会为您生成整个属性更改样板代码,从而减少需要担心的代码。

【讨论】:

  • 如果有Clazz.getWhatever(String stuff) 的方法,您似乎可以通过这种方法得到误报。在这种情况下,该方法接受一个参数,因此不代表一个属性。但是assert getName(c.&amp;whatever) == 'whatever' 仍然通过。也许getName() 方法中的附加断言,比如assert it.parameterTypes.size() == 0,将是有序的
  • @BalRog 是的,还可以检查所有者/代表的属性,如果那里有东西,这可能更容易,但需要找到{}
  • 真实数据。这里还有一个可能的错误。我还没有确定,但我认为如果属性名称是单个字符,则子表达式 ${m[1..-1]} 将引发某种“索引越界”异常。过去我不得不使用像${m[1..&lt;m.size()]} 这样笨拙的东西来解决这个问题。
  • @BalRog 或简而言之:as yuk as ;)
【解决方案2】:

对我来说,最接近提供此功能的感觉完全是做作、笨拙和临时。它依赖于 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"

哇!

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-12-19
    • 2012-12-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多