将成员与其字符串名称链接起来的最有效方法是什么?

时间:2011-05-04 19:23:40

标签: c# .net

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

  • ArgumentException使用违规变量的名称
  • DependencyProperty使用其支持的媒体资源的名称
  • INotifyPropertyChanged使用刚更改的属性名称。

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

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

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

4 个答案:

答案 0 :(得分:4)

您可以使用Expression语句链接名称。

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

表达式遵循Expression<Func<TSource, TValue>>。然后,重构和实际上没有改变表达式。然后,您的异常将解析表达式并从属性中提取名称。

答案 1 :(得分:3)

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

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

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

答案 2 :(得分:1)

您可以使用Expression<Func<T, object>>执行此操作,如下所示:

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方法检查提供的表达式并提取您指定的成员的名称。这样,当您重命名属性并重构更改时,所有这些lambda都会自动更新。不需要更多字符串了!

答案 3 :(得分:0)

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

void MyMethod([NotNull] object arg1) {}

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

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