15

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

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

我遇到的问题是,在编译时很难分辨上面的行和下面的行之间的区别:

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

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

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

甚至

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

会很甜!

有任何想法吗?

4

6 回答 6

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);
于 2009-04-27T20:48:05.810 回答
3

为了使基于表达式的 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);

    }

好的,使用作为扩展方法确实是为了方便,因为您实际上可以在一个类上调用该方法来获取另一个类的属性。我确信它可以改进。

于 2012-10-26T14:47:54.387 回答
3

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

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

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));
于 2021-04-08T10:27:53.790 回答
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 参数,以执行反射性操作,例如将属性名称作为字符串获取;或者,让委托人完成你需要的数据的工作。

于 2009-04-27T20:53:29.447 回答
1

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

您应该在您的类中将这些字符串声明为 const。

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 将能够向您显示您班级中的属性名称,作为自动完成的建议。

于 2009-04-27T20:47:28.510 回答
1

你在找什么叫做静态反射。无耻的插件 => http://emiajnet.blogspot.com/2009/05/getting-fun-with-net-static-reflection.html 还有一篇更好的文章: http ://www.lostechies.com/blogs/ gabrielschenker/archive/2009/02/03/dynamic-reflection-versus-static-reflection.aspx

于 2009-09-09T17:18:44.710 回答