【问题标题】:What is the most robust way of linking members with their string name?将成员与其字符串名称联系起来最可靠的方法是什么?
【发布时间】:2011-05-05 20:48:30
【问题描述】:

.NET 框架的各个部分都需要使用属性的字符串名称:

  • ArgumentException 使用有问题的变量的名称
  • DependencyProperty 使用它所支持的属性的名称
  • INotifyPropertyChanged 使用刚刚更改的属性的名称。

填充这些参数的最简单方法似乎是对它们进行硬编码(即:new ArgumentNullException("myArg"))。这似乎过于脆弱,直到运行时您才会意识到重构破坏了关联。

使用反射来验证这些参数是我唯一想到的解决方案,但说验证直到只在运行时执行。

有没有更好的方法来定义成员与其名称之间的关系?将优先考虑简单而优雅的设计时执行。

【问题讨论】:

  • 对于INotifyPropertyChanged,有些人使用Expressions 对其属性进行强类型化,然后将它们适当地转换回字符串表示形式。见stackoverflow.com/questions/5780232/…
  • 这些通常被称为“魔术字符串”,而 IMO 是 .NET 中令人不安的趋势。 Silverlight 和 ASP.NET MVC 都非常依赖它们。有一些方法可以绕过它们(例如,我强烈推荐 BoltClock 的建议),但我希望 MS 能找到一种方法,一开始就不介绍它们。

标签: c# .net


【解决方案1】:

您可以使用表达式语句来链接名称。

throw new SomeException<MyType>(x => x.myArg)

表达式跟在Expression&lt;Func&lt;TSource, TValue&gt;&gt; 之后。然后,重构并没有真正改变表达式。然后,您的异常会解析表达式并从属性中提取名称。

【讨论】:

【解决方案2】:

你可以只写你的反射代码来做验证,然后run it in a post-build step。这样,验证步骤就成为编译过程的一部分,并且报告的非常像编译错误。

(但是,请注意a bug in Visual Studio,因为它仍然可以通过简单地再次按“运行”而不进行任何更改来运行失败的构建...)

您打算如何进行验证?如果您只想使用 .NET 的内置反射功能,则必须将方法 IL 作为原始字节进行检查。 This ILReader class 将帮助您将这些字节转化为可以分析的有意义的东西。

【讨论】:

    【解决方案3】:

    您可以使用Expression&lt;Func&lt;T, object&gt;&gt; 执行此操作,如下所示:

    using System;
    using System.Linq;
    using System.Linq.Expressions;
    
    namespace ConsoleApplication3
    {
        public class MyClass
        {
            public int IntegralValue { get; set; }
    
            public void Validate()
            {
                if (this.IntegralValue < 0)
                    throw new ArgumentOutOfRangeException(PropertyHelper.GetName<MyClass>(o => o.IntegralValue));
            }
        }
    
        public static class PropertyHelper
        {
            /// <summary>Extracts the property (member) name from the provided expression.</summary>
            public static string GetName<T>(this Expression<Func<T, object>> expression)
            {
                MemberExpression memberExpression = null;
    
                if (expression.Body is MemberExpression)
                    memberExpression = (MemberExpression)expression.Body;
                else if (expression.Body is UnaryExpression)
                    memberExpression = (((UnaryExpression)expression.Body).Operand as MemberExpression);
    
                if (memberExpression == null)
                    throw new ApplicationException("Could not determine member name from expression.");
    
                return memberExpression.Member.Name;
            }
        }
    
        public static class Program
        {
            public static void Main(string[] args)
            {
                MyClass good = new MyClass() { IntegralValue = 100 };
                MyClass bad = new MyClass() { IntegralValue = -100 };
    
                try { good.Validate(); }
                catch (Exception ex)
                {
                    Console.WriteLine(ex);
                }
    
                try { bad.Validate(); }
                catch (Exception ex)
                {
                    Console.WriteLine(ex);
                }
    
                Console.ReadKey();
            }
        }
    }
    

    输出

    System.ArgumentOutOfRangeException: Specified argument was out of the range of valid values.
    Parameter name: IntegralValue
        at ConsoleApplication3.MyClass.Validate() in d:\...\ConsoleApplication3\Program.cs:line 14
        at ConsoleApplication3.Program.Main(String[] args) in d:\...\ConsoleApplication3\Program.cs:line 50
    

    说明

    这将允许您使用 lambda 来引用属性名称。 GetName 方法检查提供的表达式并提取您指定的成员的名称。这样,当您重命名属性并重构更改时,所有这些 lambdas 都会自动更新。不再需要字符串!

    【讨论】:

      【解决方案4】:

      我曾经使用 PostSharp(.net 的“AOP”框架)将分散的部分放在一个位置(arg 及其名称),例如:

      void MyMethod([NotNull] object arg1) {}
      

      会自动生成 MSIL 代码(在编译时)进行验证并抛出异常。

      它解决了您的示例,它可能适用于其他相关场景。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2010-10-14
        • 2018-12-09
        • 1970-01-01
        • 1970-01-01
        • 2014-09-18
        • 2011-11-23
        • 1970-01-01
        相关资源
        最近更新 更多