请问:如何得到类的属性名的这个字符串?例如,模型类 Person 有一个属性叫 Age ,我在前台绑定时写的是 Eval("Age"),如何可以写成这样 Eval(GetText(Person.Age)) ?
源由:我是一个爱重构的人,有一天我将数据库 Person 表的字段名 Age 改成了 FAge ,因模型类 Person 是跟据数据库生成的,属性名 Age 自然就变成了 FAge 了。改成这样后,分布在前台各个位置的 Eval("Age") 全部都要查找出来改为 Eval("FAge"),不然在运行时才报错,那就糗大了。我就想如果可以改成 Eval(GetText(Person.Age)) 这个样子,因为 Age 属性已经不存在了,重构后编译时就会报错,点击错误行一个一个改过来就好。
楼主这个问题我半年前也问过朋友,没有答案,后来一个不小心才找到的,很偏僻的地方有个家伙写了篇博文。
可以实现这种效果:
Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load Dim myString = Reflection.GetPropertyName(Function(c As TestClass) c.Title) MessageBox.Show(myString) '此时MyString="Title" End Sub Public Class TestClass Public Property Message As String Public Property Title As Integer End Class
楼主如果用的是C#,应该也没有关系,你看这段代码没有出现双引号就行了。 这就是你要的,写博文的人用C#实现了多种效果,看你喜欢哪一种写法。
亲,知己难求呀!
其实我一直都没用 Eval("字符串") 的方法,只是在生成实体类时多生成一个字符串静态属性,来实现方便重构、编译时报错的目的。
(回复4楼)
提出这个问题主要还是想知道别人的怎么做的。……当词不达意时,我就手痒痒就想去改名,不管是代码的属性名或方法名、还是数据库字段名
……难道也这是一种病??还是我应该像很多人一样,不要想太多、不要改名或顶多做一个数据字典备查,或还是干脆用 ctrl+f,ctrl+h 的方法来修改?……
以下是我跟据亲提供的方法和网址(需爬墙呀),又另Google了一下下(网址和调用方法在注释里了):
1、来自stackoverflow:
using System; using System.Linq.Expressions; namespace GetClassPropertyNameText { /// <summary> /// 示例模型类 /// </summary> public class Person { public string PersonName { get; set; } public DateTime Birthday { get; set; } public int Quantity { get; set; } public bool IsMarried { get; set; } } /// <summary> /// 帮助 /// <![CDATA[ /// http://stackoverflow.com/questions/4657311/reflection-get-property-name /// ]]> /// </summary> public class Helper { /// <summary> /// requires object instance, but you can skip specifying T /// <![CDATA[ /// 示例: /// Person p = new Person(); /// Helper.GetPropertyName(() => p.PersonName) /// ]]> /// </summary> /// <typeparam name="T"></typeparam> /// <param name="exp"></param> /// <returns></returns> public static string GetPropertyName<T>(Expression<Func<T>> exp) { return (((MemberExpression) (exp.Body)).Member).Name; } /// <summary> /// requires explicit specification of both object type and property type /// <![CDATA[ /// 示例: /// Helper.GetPropertyName<Person,DateTime>(p => p.Birthday) /// ]]> /// </summary> /// <typeparam name="TObject"></typeparam> /// <typeparam name="TResult"></typeparam> /// <param name="exp"></param> /// <returns></returns> public static string GetPropertyName<TObject, TResult>(Expression<Func<TObject, TResult>> exp) { var me = exp.Body as MemberExpression; if (me != null) { return me.Member.Name; } return null; } /// <summary> /// requires explicit specification of object type /// </summary> /// <typeparam name="TObject"></typeparam> /// <param name="exp"></param> /// <returns></returns> public static string GetPropertyName<TObject>(Expression<Func<TObject, object>> exp) { return (((MemberExpression) (exp.Body)).Member).Name; } } }
2、来自 github 上的一个源码:
using System; using System.Diagnostics; using System.Linq.Expressions; using System.Reflection; using GetClassPropertyNameText; namespace MuonLab.Commons.Reflection { /// <summary> /// https://github.com/trullock/MuonKit/blob/master/src/MuonLab.Commons/Reflection/ReflectionHelper.cs /// https://github.com/trullock/MuonKit/blob/master/src/MuonLab.Commons.Tests/Reflection/GetPropertyName.cs /// <![CDATA[ /// ReflectionHelper.GetPropertyName<TestClass, string>(t => t.Name) /// ReflectionHelper.GetPropertyName<TestClass, int>(t => t.InnerClass.Age) /// ]]> /// </summary> public static class ReflectionHelper { public static string GetPropertyName<T, TResult>(Expression<Func<T, TResult>> property) { var body = GetBody(property); return body.Member.Name; } public static MemberInfo GetMemberInfo<T, TResult>(Expression<Func<T, TResult>> property) { var body = GetBody(property); return body.Member; } public static TValue GetPropertyValue<TEntity, TValue>(TEntity entity, Expression<Func<TEntity, TValue>> property) { return property.Compile().Invoke(entity); } public static void SetPropertyValue<TEntity, TProperty>(TEntity entity, Expression<Func<TEntity, TProperty>> property, TProperty value) { typeof(TEntity).GetProperty(GetPropertyName(property)).SetValue(entity, value, null); } public static object GetPropertyValue(object obj, string propertyName) { return obj.GetType().GetProperty(propertyName).GetValue(obj, null); } public static string PropertyChainToString<TEntity, TResult>(Expression<Func<TEntity, TResult>> property, char delimeter) { var body = GetBody(property); return propertyChainToString(body.Expression, delimeter) + body.Member.Name; } public static string PropertyChainToString(Expression expression, char delimeter) { if (expression is LambdaExpression) { var memberExpression = expression as LambdaExpression; return propertyChainToString(memberExpression.Body, delimeter).TrimEnd(delimeter); } throw new NotSupportedException("Probably a nullable type, need implementing! Debug: " + expression); } private static string propertyChainToString(Expression expression, char delimeter) { if (expression is MemberExpression) { var exp = expression as MemberExpression; return propertyChainToString(exp.Expression, delimeter) + exp.Member.Name + delimeter; } if (expression is MethodCallExpression) { var exp = expression as MethodCallExpression; if (exp.Method.Name == "get_Item") { var index = ((exp.Arguments[0] as MemberExpression).Member as FieldInfo).GetValue(((exp.Arguments[0] as MemberExpression).Expression as ConstantExpression).Value); return (exp.Object as MemberExpression).Member.Name + "[" + index + "]" + delimeter; } } return string.Empty; } private static MemberExpression GetBody<TEntity, TResult>(Expression<Func<TEntity, TResult>> property) { if ((typeof(TResult).IsGenericType && typeof(TResult).GetGenericTypeDefinition().Equals(typeof(Nullable<>))) && property.Body is UnaryExpression) { var outerBody = property.Body as UnaryExpression; return outerBody.Operand as MemberExpression; } return property.Body as MemberExpression; } public static Expression<Func<T, TValue>> BuildGet<T, TValue>(PropertyInfo property) { Type type = typeof(T); ParameterExpression arg = Expression.Parameter(type, "x"); var prop = Expression.Property(arg, property); return Expression.Lambda<Func<T, TValue>>(prop, arg); } public static bool MemberAccessExpressionsAreEqual<T, TProperty>(Expression<Func<T, TProperty>> expression1, Expression<Func<T, TProperty>> expression2) { return memberAccessExpressionsAreEqual(expression1.Body as MemberExpression, expression2.Body as MemberExpression); } private static bool memberAccessExpressionsAreEqual(MemberExpression expression1, MemberExpression expression2) { if (expression1 != null && expression2 != null) if (expression1.Member.Name == expression2.Member.Name) return memberAccessExpressionsAreEqual(expression1.Expression as MemberExpression, expression2.Expression as MemberExpression); else return false; else return true; } } /// <summary> /// /// </summary> public class Test2 { public void Test() { string s = ReflectionHelper.GetPropertyName<Person, string>(t => t.PersonName); Debug.WriteLine(s); } } }
@佬唐:
这个事情是我半年前在研究CSLA.NET时,逐渐清晰的一个想法。
在CSLA的三层架构中,无法象LINQ一样选择性回传一个对象列表中的少量字段,
var query = from c in EntityType
select c.Title, c.Message, c.BillDate
我也知道可以通过改造,加一个参数,传入字段字符串,但真的很不爽。
于是萌生了这样一个想法,但当时不知道怎么Google,问朋友也不知道。
我希望实现的类似这样
function GetClassList(byval Type as EntityType,
param FieldName as string())
调用时,希望FieldName是可以以 c.Title, c.Message, C.BillDate 这样强类型方式书写的。
不行。你需要把 GetText 改成这样:GetText(Person obj,string propertyName);
终于看到大神也有失手的时候了。呵呵。
这个问题我以前也Google了很久,很不容易找,基本上得在几百万篇文章里面才能找到一篇讲这事的。
普通人又不理解为什么,楼主其实已经说出原因了。
通过这个方法,可以避免类名(主要是数据库字段名的映射)修改后,代码中使用"FieldName"这种方法的编译不出错,而如果有类似楼主要求的方法,则可以在编译阶段显示错误。
这个其实是类似LINQ的强类型方法。
多谢回复!
呵呵,还真的有点失手,因为还是是要我输入string,输入双引号,我要的效果是可以在敲代码时“点”出来。
@佬唐: 我不知道为什么失手了,如果他们回答对了,你就把他们的答案标记为正确就 OK 了。
@爱编程的大叔: 不知道你们还在不在,上面的算看懂了也好像不是很懂,如果都是在程序运行时获取字符串,我不知道性能怎么样,我的强症是想要个类似于nameof(ClassA.PropertyB)这样的效果,直接编译阶段就变成常量的字符串。不知道有没有这种方法?
@给个面子吧: 代码上面提供了啊,而且现在新的Framework有Nameof。
至于性能,给你一台银河超算,你用来计算二位数加法,需要考虑代码的性能么?
性能是一个很复杂的话题,一言难尽。
@爱编程的大叔: 上面的代码使用中,非常感谢。昨天的问题有点傻。性能什么的,代码好好写一下就有了。
@爱编程的大叔: 刚刚差了下,C#6 有nameof,这种关键字是编译器提供的,无关Framework,要安装VS2015以上。看来VS2010是有点用太老了。。。。。
用VS的解决方案内全部文件替换
还是谢谢你的回复,尽管我们都知道可以这么做。
你可以把绑定字段用 枚举列好,取的话你懂的,改的话改下枚举里面的值就行了,这也算可扩展的写法
另外数据库字段修改很少吧,定义好后,一般字段也只是新增数据库字段,改数据库字段名称有点强迫症的行为
谢谢回复……我现在的方法就是在代码模板里生成“属性的代码”时,同时生成一个“属性名的字符串”静态成员,供绑定表达式调用。
public int Age{get;set;} public static readonly string Age_Text="Age";
//绑定表达式 Eval(Person.Age_Text)
这样数据库重构后重新生成实体类,绑定表达式就会在编译时报错,等待修改。
我承认我有点强迫症,但当需求变化,原名词与其需真实表达的意义不符合时,重构它我认为应该是正确的做法吧?
你可以试试用 反射 来做,不过有性能上的影响, 简单点的你可以用 EF
谢谢,前台绑定模型对象、绑定DataTalbe、EF……我就是不想限定……
用 Eval 就不管传过来的是 DataTable , 还是模型对象,前台不用改一个代码。
解决问题的方案很多:
1、继续保持Age的属性名,而在实体映射的时候,指向不同的数据库字段
2、继续保持Age属性,忽略数据库映射,同时把Age作为FAge的代理
3、自己重写Eval函数实现,同时把调用换个方式,比如使用代理、表达式
4、自己重写Binder,这个工作量更大。
谢谢回复,是有很多“高级”层面的方法解决绑定时继续使用原来那个字符串的方法,但我的问题还是如何“得到属性名这个字符串”,在绑定表达式里“不要存在双引号”。
不错。学习了。