C# 使用Intellisense和编译时检查提取反射的属性名
嗯。因此,我有一些代码将winForm上的某些控件映射到对象中的某些属性,以便在数据发生某些情况时对控件执行某些操作。一切都很好,很好用。不是问题。问题是,要向映射添加项,我调用如下函数:C# 使用Intellisense和编译时检查提取反射的属性名,c#,reflection,mapping,intellisense,compile-time,C#,Reflection,Mapping,Intellisense,Compile Time,嗯。因此,我有一些代码将winForm上的某些控件映射到对象中的某些属性,以便在数据发生某些情况时对控件执行某些操作。一切都很好,很好用。不是问题。问题是,要向映射添加项,我调用如下函数: this.AddMapping(this.myControl,myObject,"myObjectPropertyName"); 我遇到的问题是,在编译时很难区分上面的行和下面的行之间的区别: this.AddMapping(this.myControl,myObject,"myObjectProprety
this.AddMapping(this.myControl,myObject,"myObjectPropertyName");
我遇到的问题是,在编译时很难区分上面的行和下面的行之间的区别:
this.AddMapping(this.myControl,myObject,"myObjectPropretyName");
因为最后一个参数是一个字符串,所以没有编译时检查或类似的检查来强制字符串本身实际上对应于给定对象上的有效属性名。此外,诸如重构和“查找所有引用”之类的东西会错过这种引用,当属性本身的名称发生更改时,会导致欢闹。所以我想知道的是,是否有某种方法可以改变函数,使我传入的仍然是一个以某种方式表示属性名的字符串,但是编译时会检查实际值。有人说我可以用表达式树来实现这一点,但我读过它们,似乎看不到它们之间的联系。我想做一些类似的事情:
this.AddMapping(this.myControl,myObject,myObject.myObjectPropertyName);
甚至
this.AddMapping(this.myControl,myObject.myObjectPropertyName);
那太好了
有什么想法吗?您真的不应该将字符串文本作为属性名传入。相反,您应该使用
YourClass.PROPERTY\u NAME\u 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
这将阻止未引用有效属性的字符串进入函数调用
IntelisSense将能够显示类中的属性名称,作为自动完成的建议。在3.5中,表达式是将成员名称指定为代码的一种方式;你可以:
public void AddMapping<TObj,TValue>(Control myControl, TObj myObject,
Expression<Func<TObj, TValue>> mapping) {...}
为此,请考虑使用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);
如果需要在运行时分解抽象语法树,可以使用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;
}
好的,使用作为扩展方法确实是为了方便,因为实际上您可以对一个类调用该方法以获取另一个类的属性。我相信它可以改进。这是一个老问题,但没有人用最新的可用语法来更新答案 从C#6开始,如果在获取成员名称的同时仍然引用该成员本身,则有一种更好的方法。关键字的
名称。它是一种语法糖,将替换为包含所引用的属性/成员/函数名称的字符串。智能感知、重构、查找所有引用都可以工作。这将适用于任何属性、字段、函数和类引用
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));
我花了6到7遍才发现你两行代码的不同。欢迎来到我的地狱。。。现在想象一下,它到处都是首字母缩略词,比如CPCR、CPR、CLI等等……我一直希望有一种VS插件能够解析代码中的所有字符串并对它们进行拼写检查。它还将考虑驼峰大小写,并单独检查每个单词的拼写。有人需要写那个傻瓜…@BFree-Resharper的代理Smith插件就是这样做的,但是一个免费的选项是从这里获得DXCore:并从这里添加拼写检查插件:。虽然我不确定它是否支持字符串中的CamelCase…@BFree-no,但我们需要的是缺少的nameof(Foo)
或memberof(Foo)
关键字/方法。我们一直在要求和希望,但什么都没有。在那之前,表达方式是我所知道的最接近的表达方式。我想到了,唯一困扰我的是它的冗余。。。您现在在两个不同的位置(常量字符串和属性名称本身)维护属性信息,这似乎不太理想。您真的不必使用字符串。你可以把它们映射到int或者其他什么。名称很重要,而且值是唯一的。但是如果我不将它们映射到字符串,如何最终获得字符串值?如果您需要某种字符串文本进行打印,那么您就必须使用字符串。否则,始终使用类中的属性名来引用属性,而不是int/string值。没有理由显示属性名的文本值,只要知道属性名,就不需要任何更多信息。我可以看到一些与扩展方法的使用相关的注释,我之所以想弄到这个,是因为WPF绑定(mvvm),所以您可以调用OnPropertyChanged(this.GetProperty(()=>MyProperty);从而有助于缓解此类wpf绑定的编译时安全性方面的一些问题。至少,如果您更改属性名称,您将获得一些编译时安全性。不过,您仍然需要在xaml中编辑绑定文本。。。
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);
}
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() { }
}
this.AddMapping(this.myControl, nameof(myObject.myObjectPropertyName));