使用Intellisense和编译时检查提取反射的属性名称

时间:2009-04-27 20:43:08

标签: c# reflection mapping intellisense compile-time

确定。所以我有一些代码将winForm上的某些控件映射到对象中的某些属性,以便在数据发生某些事情时对控件执行某些操作。一切都很好,工作得很好。不是问题。问题是,要将项添加到映射中,我调用的函数如下:

this.AddMapping(this.myControl,myObject,"myObjectPropertyName");

我遇到的问题是,在编译时很难说出上述行与下面的行之间的区别:

this.AddMapping(this.myControl,myObject,"myObjectPropretyName");

由于最后一个参数是一个字符串,因此没有编译时检查或类似的任何操作会强制字符串本身实际上对应于给定对象上的有效属性名称。此外,Refactor和“查找所有引用”之类的内容错过了这种引用,当属性名称发生变化时会导致欢闹。所以我想知道的是,是否有某种方法可以更改函数,以便我传入的内容仍然是以某种方式表示属性名称的字符串,但是使用编译时检查实际值是否进入。有人说我可以用Expression Trees做到这一点,但我已经阅读了它们,似乎没有看到连接。我喜欢做类似的事情:

this.AddMapping(this.myControl,myObject,myObject.myObjectPropertyName);

甚至

this.AddMapping(this.myControl,myObject.myObjectPropertyName);

会很甜蜜!

有什么想法吗?

6 个答案:

答案 0 :(得分:15)

在3.5中,Expression是一种将成员名称指定为代码的方法;你可以:

public void AddMapping<TObj,TValue>(Control myControl, TObj myObject,
       Expression<Func<TObj, TValue>> mapping) {...}

然后解析表达式树以获取值。效率不高,但也不算太差。

以下是示例代码:

    public void AddMapping<TSource, TValue>(
        Control control,
        TSource source,
        Expression<Func<TSource, TValue>> mapping)
    {
        if (mapping.Body.NodeType != ExpressionType.MemberAccess)
        {
            throw new InvalidOperationException();
        }
        MemberExpression me = (MemberExpression)mapping.Body;
        if (me.Expression != mapping.Parameters[0])
        {
            throw new InvalidOperationException();
        }
        string name = me.Member.Name;
        // TODO: do something with "control", "source" and "name",
        // maybe also using "me.Member"
    }

呼叫:

    AddMapping(myControl, foo, f => f.Bar);

答案 1 :(得分:3)

为了使基于Expression的lamda解决方法更容易,我将其作为扩展方法编写。

  public static string GetPropertyName<T>(this object o, Expression<Func<T>> property)
    {
        var propertyInfo = (property.Body as MemberExpression).Member as PropertyInfo;
        if (propertyInfo == null)
            throw new ArgumentException("The lambda expression 'property' should point to a valid Property");
        var propertyName = propertyInfo.Name;
        return propertyName;
    }

像这样打电话

    class testclass
    {
        public string s { get; set; }
        public string s2 { get; set; }
        public int i { get; set; }

    }

    [TestMethod]
    public void TestMethod2()
    {
        testclass x = new testclass();
        string nameOfPropertyS = this.GetPropertyName(() => x.s);
        Assert.AreEqual("s", nameOfPropertyS);

        string nameOfPropertyI = x.GetPropertyName(() => x.i);
        Assert.AreEqual("i", nameOfPropertyI);

    }

好吧,使用扩展方法实际上是为了方便,因为事实上你可以在一个类上为anther类的属性调用该方法。我相信它可以改进。

答案 2 :(得分:2)

考虑使用lambdas甚至System.Linq.Expressions,其中之一是:

extern void AddMapping<T,U>(Control control, T target, Func<T,U> mapping);
extern void AddMapping<T,U>(Control control, T target, Expression<Func<T,U>> mapping);

然后用

调用它
this.AddMapping(this.myControl, myObject, (x => x.PropertyName));

如果需要在运行时拆分抽象语法树,请使用Expression参数来执行反射性操作,例如将属性名称作为字符串获取;或者,让代表完成捕获所需数据的工作。

答案 3 :(得分:1)

你真的不应该将String文字作为属性名传递。相反,您应该使用YourClass.PROPERTY_NAME_FOO

您应该将这些字符串声明为您班级中的等级。

public const String PROPERTY_NAME_FOO = "any string, it really doesn't matter";
public const String PROPERTY_NAME_BAR = "some other name, it really doesn't matter";

或者,您根本不必担心字符串,只需要输入属性名称:

public const int PROPERTY_NAME_FOO = 0;
public const int PROPERTY_NAME_BAR = 1; //values don't matter, as long as they are unique

这将阻止不引用有效属性的字符串进入函数调用。

Intelisense将能够向您显示班级中的属性名称,作为自动完成的建议。

答案 4 :(得分:1)

答案 5 :(得分:1)

老问题,但没有人用最近可用的语法糖更新答案。

从 C# 6 开始,如果获取成员名称,同时仍然引用成员本身,则有更好的方法。 nameof 关键字。它是一种语法糖,将替换为包含所引用的属性/成员/函数的名称的字符串。智能感知、重构、查找所有引用都将起作用。这适用于任何属性、字段、函数和类引用。

using System;

public class Program
{
    public static void Main()
    {
        Console.WriteLine("Name of the class is: {0}", nameof(MyClass));
        Console.WriteLine("Name of the class field is: {0}", nameof(MyClass.MyField));
        Console.WriteLine("Name of the class property is: {0}", nameof(MyClass.MyProperty));
        Console.WriteLine("Name of the class function is: {0}", nameof(MyClass.MyFunction));
    }
}

public class MyClass
{
    public int MyField;

    public int MyProperty { get; set; }

    public void MyFunction() { }
}

这个程序输出以下几行:

<块引用>

类名是:MyClass
类字段的名称是:MyField
类属性的名称是:MyProperty
类函数的名称是:MyFunction

使用它,您的代码现在变成:

this.AddMapping(this.myControl, nameof(myObject.myObjectPropertyName));