首页 新闻 会员 周边

如何得到类的属性名的这个字符串?

0
悬赏园豆:5 [已解决问题] 解决于 2014-10-15 19:59

请问:如何得到类的属性名的这个字符串?例如,模型类 Person 有一个属性叫 Age ,我在前台绑定时写的是 Eval("Age"),如何可以写成这样 Eval(GetText(Person.Age)) ?

源由:我是一个爱重构的人,有一天我将数据库 Person 表的字段名 Age 改成了 FAge ,因模型类 Person 是跟据数据库生成的,属性名 Age 自然就变成了 FAge 了。改成这样后,分布在前台各个位置的 Eval("Age") 全部都要查找出来改为 Eval("FAge"),不然在运行时才报错,那就糗大了。我就想如果可以改成 Eval(GetText(Person.Age)) 这个样子,因为 Age 属性已经不存在了,重构后编译时就会报错,点击错误行一个一个改过来就好。

佬唐的主页 佬唐 | 菜鸟二级 | 园豆:205
提问于:2014-10-13 12:42
< >
分享
最佳答案
1

楼主这个问题我半年前也问过朋友,没有答案,后来一个不小心才找到的,很偏僻的地方有个家伙写了篇博文

可以实现这种效果:

 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#实现了多种效果,看你喜欢哪一种写法。

收获园豆:5
爱编程的大叔 | 高人七级 |园豆:30839 | 2014-10-13 13:28

亲,知己难求呀!

其实我一直都没用 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);
        }
    }
}

 

 

 

佬唐 | 园豆:205 (菜鸟二级) | 2014-10-14 22:43

@佬唐: 

这个事情是我半年前在研究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 这样强类型方式书写的。

爱编程的大叔 | 园豆:30839 (高人七级) | 2014-10-15 07:50
其他回答(6)
0

不行。你需要把 GetText 改成这样:GetText(Person obj,string propertyName);

Launcher | 园豆:45045 (高人七级) | 2014-10-13 13:03

终于看到大神也有失手的时候了。呵呵。

这个问题我以前也Google了很久,很不容易找,基本上得在几百万篇文章里面才能找到一篇讲这事的。

普通人又不理解为什么,楼主其实已经说出原因了。

通过这个方法,可以避免类名(主要是数据库字段名的映射)修改后,代码中使用"FieldName"这种方法的编译不出错,而如果有类似楼主要求的方法,则可以在编译阶段显示错误。

这个其实是类似LINQ的强类型方法。

支持(1) 反对(0) 爱编程的大叔 | 园豆:30839 (高人七级) | 2014-10-13 13:26

多谢回复!

呵呵,还真的有点失手,因为还是是要我输入string,输入双引号,我要的效果是可以在敲代码时“点”出来。

支持(0) 反对(0) 佬唐 | 园豆:205 (菜鸟二级) | 2014-10-14 20:19

@佬唐: 我不知道为什么失手了,如果他们回答对了,你就把他们的答案标记为正确就 OK 了。

支持(0) 反对(0) Launcher | 园豆:45045 (高人七级) | 2014-10-15 09:39

@爱编程的大叔: 不知道你们还在不在,上面的算看懂了也好像不是很懂,如果都是在程序运行时获取字符串,我不知道性能怎么样,我的强症是想要个类似于nameof(ClassA.PropertyB)这样的效果,直接编译阶段就变成常量的字符串。不知道有没有这种方法?

支持(0) 反对(0) 给个面子吧 | 园豆:200 (初学一级) | 2017-05-01 00:26

@给个面子吧: 代码上面提供了啊,而且现在新的Framework有Nameof。

至于性能,给你一台银河超算,你用来计算二位数加法,需要考虑代码的性能么?

性能是一个很复杂的话题,一言难尽。

支持(0) 反对(0) 爱编程的大叔 | 园豆:30839 (高人七级) | 2017-05-02 09:25

@爱编程的大叔: 上面的代码使用中,非常感谢。昨天的问题有点傻。性能什么的,代码好好写一下就有了。

支持(0) 反对(0) 给个面子吧 | 园豆:200 (初学一级) | 2017-05-02 09:43

@爱编程的大叔: 刚刚差了下,C#6 有nameof,这种关键字是编译器提供的,无关Framework,要安装VS2015以上。看来VS2010是有点用太老了。。。。。

支持(0) 反对(0) 给个面子吧 | 园豆:200 (初学一级) | 2017-05-02 09:52
0

用VS的解决方案内全部文件替换

Cherbim | 园豆:323 (菜鸟二级) | 2014-10-13 13:19

还是谢谢你的回复,尽管我们都知道可以这么做。

支持(0) 反对(0) 佬唐 | 园豆:205 (菜鸟二级) | 2014-10-14 20:15
0

   你可以把绑定字段用 枚举列好,取的话你懂的,改的话改下枚举里面的值就行了,这也算可扩展的写法

      另外数据库字段修改很少吧,定义好后,一般字段也只是新增数据库字段,改数据库字段名称有点强迫症的行为

风醉 | 园豆:1197 (小虾三级) | 2014-10-13 13:50

谢谢回复……我现在的方法就是在代码模板里生成“属性的代码”时,同时生成一个“属性名的字符串”静态成员,供绑定表达式调用。

public int Age{get;set;}
public static readonly string Age_Text="Age";

//绑定表达式 Eval(Person.Age_Text)

这样数据库重构后重新生成实体类,绑定表达式就会在编译时报错,等待修改。

我承认我有点强迫症,但当需求变化,原名词与其需真实表达的意义不符合时,重构它我认为应该是正确的做法吧?

支持(0) 反对(0) 佬唐 | 园豆:205 (菜鸟二级) | 2014-10-14 20:14
0

 你可以试试用 反射 来做,不过有性能上的影响, 简单点的你可以用 EF

jerry128 | 园豆:31 (初学一级) | 2014-10-13 14:37

谢谢,前台绑定模型对象、绑定DataTalbe、EF……我就是不想限定……

 

用 Eval 就不管传过来的是 DataTable , 还是模型对象,前台不用改一个代码。

支持(0) 反对(0) 佬唐 | 园豆:205 (菜鸟二级) | 2014-10-14 22:49
0

解决问题的方案很多:

1、继续保持Age的属性名,而在实体映射的时候,指向不同的数据库字段

2、继续保持Age属性,忽略数据库映射,同时把Age作为FAge的代理

3、自己重写Eval函数实现,同时把调用换个方式,比如使用代理、表达式

4、自己重写Binder,这个工作量更大。

519740105 | 园豆:5810 (大侠五级) | 2014-10-13 15:38

谢谢回复,是有很多“高级”层面的方法解决绑定时继续使用原来那个字符串的方法,但我的问题还是如何“得到属性名这个字符串”,在绑定表达式里“不要存在双引号”。

支持(0) 反对(0) 佬唐 | 园豆:205 (菜鸟二级) | 2014-10-14 20:00
0

不错。学习了。

zoti | 园豆:187 (初学一级) | 2017-10-12 10:50
清除回答草稿
   您需要登录以后才能回答,未注册用户请先注册