【问题标题】:Can I write the method name without using a String to pass the name of the method? (c#)我可以在不使用字符串传递方法名称的情况下编写方法名称吗? (C#)
【发布时间】:2014-12-12 10:37:24
【问题描述】:

我有这门课:

class foo
{
   int val;
   public int Val
   {
      set{ val = values; },
      set{ val = values; }
   }
}

我需要将属性名称传递给 DataBinding:

String propertyName = "Val";
ctrl.DataBindings.Add(propertyName, object, dataMember, true, DataSourceUpdateMode.Never);

我想做这样的事情:

propertyName = typeof(foo).methods.Val.toString();

【问题讨论】:

    标签: c#


    【解决方案1】:

    如果您可以使用 C#6,您就有 nameof 运算符,它就是这样做的。

    string propertyName = nameof(foo.Val);
    

    如果您使用 C# 5,则可以利用表达式树:

    public static string GetPropertyName<TParent>(Expression<Func<TParent, object>> prop)
    {
        var expr = prop.Body;
    
        if (expr.NodeType == ExpressionType.Convert)
            expr = ((UnaryExpression)expr).Operand;
    
        if (expr.NodeType == ExpressionType.MemberAccess)
            return ((MemberExpression)expr).Member.Name;
    
        throw new ArgumentException("Invalid lambda", "prop");
    }
    

    像这样使用这个辅助函数(假设它在 ReflectionHelper 类中):

    string propertyName = ReflectionHelper.GetPropertyName<foo>(x => x.Val);
    

    这样,您可以在 IDE 中安全地使用重构。

    【讨论】:

    • 谢谢,此解决方案适用于 4.5 .NET。我想使用这段代码,uscation 字符串不会破坏程序。
    • @dlopezgonzalez 是的,在这种情况下,表达式树方法不应该中断,因为它引用了程序集元数据。但是如果你在构建之后通过混淆更改属性名称,nameof 方法仍然会中断。
    • 我希望,例如,旧的 eazfuscator 在使用反射时会破坏代码。为了避免这种情况,我需要在类“[System.Reflection.ObfuscationAttribute(Feature = "properties renaming", Exclude = true)]”的顶部添加这个。我希望这对离开这里的另一个人有所帮助。
    【解决方案2】:

    如果您使用的不是 C# 6,则需要传递 Expression&lt;Func&lt;T&gt;&gt;

    然后您可以使用该对象执行此操作(如果您正在传递属性):

     private string GetPropertyName(Expression<Func<T>> propertyExpession)
     {
       //the cast will always succeed if properly used
       MemberExpression memberExpression = (MemberExpression)propertyExpression.Body;
       string propertyName = memberExpression.Member.Name;
       return propertyName;
     }
    

    你可以这样使用:

    var propName = GetPropertyName(() => this.Val);
    

    【讨论】:

    • 请注意,您不希望过于频繁地这样做 - 我不认为缓存表达式树。
    • 我查过了,你说得对,他们不是。这几乎与 PRISM 的 OnPropertyChanged() 实现所使用的代码相同 - 所以它不会那么糟糕,对吧? ;)
    • @JonSkeet 大多数 MVVM 框架在 INotifyPropertyChanged 实现中使用该技巧,尽管最终代码必须更加健壮。
    • @PanagiotisKanavos:这并不意味着它一定是个好主意——事实上,在 C# 5 中,无论如何你都希望使用CallerMemberNameAttribute 来代替INotifyPropertyChanged
    • 框架可以添加自己的缓存。领先的框架没有性能问题,因此它们要么缓存,要么在这种情况下问题不那么严重。 CallerMemberNameAttribute 仅限于传递调用者的姓名,因此您无法更新相关属性。只有nameof 是一个完整的选择
    【解决方案3】:

    我不知道您是否在使用 INotifyPropertyChanged,但这里有一些关于如何避免使用“魔术字符串”的文章可能有用:

    Implementing NotifyPropertyChanged without magic strings

    typesafe NotifyPropertyChanged using linq expressions

    【讨论】:

      【解决方案4】:

      从 C# 6 开始,您可以使用 nameof 运算符:

      ctrl.DataBindings.Add(nameof(foo.Val), /* other arguments as before */);
      

      在 C# 6 之前,没有真正简单的方法可以在编译时执行此操作。然而,一种选择是进行单元测试,检查所有属性名称是否都是实际属性(使用反射检查)。

      另请注意,在 C# 5 中,CallerMemberNameAttribute 对实现 INotifyPropertyChanged 很有用 - 但对您的情况没有那么有用。

      使用表达式树的方法很有效,但对我来说有点笨拙。虽然技术含量低得多,但简单的字符串常量和单元测试感觉更简单。

      【讨论】:

      • 大多数 MVVM 框架(实际上是实现 INotifyPropertyChanged 的​​部分)通过传递指向属性的表达式来工作。无需反思
      • @PanagiotisKanavos:我建议的反思将完全在 test 时间。
      • 这听起来也很奇怪——使用测试进行静态分析。几乎听起来像罗斯林诊断:)。
      • @PanagiotisKanavos:我已经计划将一些 Noda Time 单元测试转换为诊断 :)
      【解决方案5】:

      使用 LINQ 检查静态类型反射:http://blogs.clariusconsulting.net/kzu/statically-typed-reflection-with-linq/

      你可以这样做:

      string propertyName = Reflect<foo>.GetProperty(x => x.Val).Name;
      

      【讨论】:

      • 这仍然是反射,如果您可以只传递带有属性的表达式,则不需要它
      猜你喜欢
      • 1970-01-01
      • 2015-03-08
      • 2019-02-10
      • 2020-06-20
      • 2010-10-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-10-01
      相关资源
      最近更新 更多