以强types的方式获取属性的属性

美好的一天!

我有这样的方法来获得[DisplayName]属性的值(直接附加或使用[MetadataType]属性)。 在极less数情况下,我需要在控制器代码中获得[DisplayName]

 public static class MetaDataHelper { public static string GetDisplayName(Type dataType, string fieldName) { // First look into attributes on a type and it's parents DisplayNameAttribute attr; attr = (DisplayNameAttribute)dataType.GetProperty(fieldName).GetCustomAttributes(typeof(DisplayNameAttribute), true).SingleOrDefault(); // Look for [MetadataType] attribute in type hierarchy // http://stackoverflow.com/questions/1910532/attribute-isdefined-doesnt-see-attributes-applied-with-metadatatype-class if (attr == null) { MetadataTypeAttribute metadataType = (MetadataTypeAttribute)dataType.GetCustomAttributes(typeof(MetadataTypeAttribute), true).FirstOrDefault(); if (metadataType != null) { var property = metadataType.MetadataClassType.GetProperty(fieldName); if (property != null) { attr = (DisplayNameAttribute)property.GetCustomAttributes(typeof(DisplayNameAttribute), true).SingleOrDefault(); } } } return (attr != null) ? attr.DisplayName : String.Empty; } } 

它有效,但有两个缺点:

  • 它需要字段名称作为string
  • 如果我想获得一个物业的财产,这是行不通的

是否有可能克服这两个问题使用lambdas,就像我们在ASP.NET MVC:

 Html.LabelFor(m => m.Property.Can.Be.Very.Complex.But.Strongly.Typed); 

更新

这是BuildStarted解决scheme的更新和检查版本。 它被修改为使用DisplayName属性(如果使用它,可以修改回Display属性)。 并修复小错误以获取嵌套属性的属性。

 public static string GetDisplayName<TModel>(Expression<Func<TModel, object>> expression) { Type type = typeof(TModel); string propertyName = null; string[] properties = null; IEnumerable<string> propertyList; //unless it's a root property the expression NodeType will always be Convert switch (expression.Body.NodeType) { case ExpressionType.Convert: case ExpressionType.ConvertChecked: var ue = expression.Body as UnaryExpression; propertyList = (ue != null ? ue.Operand : null).ToString().Split(".".ToCharArray()).Skip(1); //don't use the root property break; default: propertyList = expression.Body.ToString().Split(".".ToCharArray()).Skip(1); break; } //the propert name is what we're after propertyName = propertyList.Last(); //list of properties - the last property name properties = propertyList.Take(propertyList.Count() - 1).ToArray(); //grab all the parent properties foreach (string property in properties) { PropertyInfo propertyInfo = type.GetProperty(property); type = propertyInfo.PropertyType; } DisplayNameAttribute attr; attr = (DisplayNameAttribute)type.GetProperty(propertyName).GetCustomAttributes(typeof(DisplayNameAttribute), true).SingleOrDefault(); // Look for [MetadataType] attribute in type hierarchy // http://stackoverflow.com/questions/1910532/attribute-isdefined-doesnt-see-attributes-applied-with-metadatatype-class if (attr == null) { MetadataTypeAttribute metadataType = (MetadataTypeAttribute)type.GetCustomAttributes(typeof(MetadataTypeAttribute), true).FirstOrDefault(); if (metadataType != null) { var property = metadataType.MetadataClassType.GetProperty(propertyName); if (property != null) { attr = (DisplayNameAttribute)property.GetCustomAttributes(typeof(DisplayNameAttribute), true).SingleOrDefault(); } } } return (attr != null) ? attr.DisplayName : String.Empty; } 

有两种方法可以做到这一点:

 Models.Test test = new Models.Test(); string DisplayName = test.GetDisplayName(t => t.Name); string DisplayName = Helpers.GetDisplayName<Models.Test>(t => t.Name); 

第一个通过写一个通用的扩展方法到任何TModel(这是所有types)的工作。 这意味着它可以在任何对象上使用,而不仅仅是您的模型。 不是真的推荐,但很好,因为它是简洁的语法。

第二种方法要求你传入模型的types – 你已经做了,但作为一个参数。 这个方法需要通过generics定义types,因为Func期望它。

以下是您检查的方法。

所有对象的静态扩展方法

 public static string GetDisplayName<TModel, TProperty>(this TModel model, Expression<Func<TModel, TProperty>> expression) { Type type = typeof(TModel); MemberExpression memberExpression = (MemberExpression)expression.Body; string propertyName = ((memberExpression.Member is PropertyInfo) ? memberExpression.Member.Name : null); // First look into attributes on a type and it's parents DisplayAttribute attr; attr = (DisplayAttribute)type.GetProperty(propertyName).GetCustomAttributes(typeof(DisplayAttribute), true).SingleOrDefault(); // Look for [MetadataType] attribute in type hierarchy // http://stackoverflow.com/questions/1910532/attribute-isdefined-doesnt-see-attributes-applied-with-metadatatype-class if (attr == null) { MetadataTypeAttribute metadataType = (MetadataTypeAttribute)type.GetCustomAttributes(typeof(MetadataTypeAttribute), true).FirstOrDefault(); if (metadataType != null) { var property = metadataType.MetadataClassType.GetProperty(propertyName); if (property != null) { attr = (DisplayAttribute)property.GetCustomAttributes(typeof(DisplayNameAttribute), true).SingleOrDefault(); } } } return (attr != null) ? attr.Name : String.Empty; } 

types特定方法的签名 – 与上面相同的代码只是不同的调用

 public static string GetDisplayName<TModel>(Expression<Func<TModel, object>> expression) { } 

你不能仅仅使用Something.GetDisplayName(t => t.Name)的原因是因为在Razor引擎中,实际上传递了一个HtmlHelper<TModel>的实例化对象,这就是为什么第一个方法需要实例化object – 这只是编译器推断哪些types属于哪个通用名称所必需的。

用recursion属性更新

 public static string GetDisplayName<TModel>(Expression<Func<TModel, object>> expression) { Type type = typeof(TModel); string propertyName = null; string[] properties = null; IEnumerable<string> propertyList; //unless it's a root property the expression NodeType will always be Convert switch (expression.Body.NodeType) { case ExpressionType.Convert: case ExpressionType.ConvertChecked: var ue = expression.Body as UnaryExpression; propertyList = (ue != null ? ue.Operand : null).ToString().Split(".".ToCharArray()).Skip(1); //don't use the root property break; default: propertyList = expression.Body.ToString().Split(".".ToCharArray()).Skip(1); break; } //the propert name is what we're after propertyName = propertyList.Last(); //list of properties - the last property name properties = propertyList.Take(propertyList.Count() - 1).ToArray(); //grab all the parent properties Expression expr = null; foreach (string property in properties) { PropertyInfo propertyInfo = type.GetProperty(property); expr = Expression.Property(expr, type.GetProperty(property)); type = propertyInfo.PropertyType; } DisplayAttribute attr; attr = (DisplayAttribute)type.GetProperty(propertyName).GetCustomAttributes(typeof(DisplayAttribute), true).SingleOrDefault(); // Look for [MetadataType] attribute in type hierarchy // http://stackoverflow.com/questions/1910532/attribute-isdefined-doesnt-see-attributes-applied-with-metadatatype-class if (attr == null) { MetadataTypeAttribute metadataType = (MetadataTypeAttribute)type.GetCustomAttributes(typeof(MetadataTypeAttribute), true).FirstOrDefault(); if (metadataType != null) { var property = metadataType.MetadataClassType.GetProperty(propertyName); if (property != null) { attr = (DisplayAttribute)property.GetCustomAttributes(typeof(DisplayNameAttribute), true).SingleOrDefault(); } } } return (attr != null) ? attr.Name : String.Empty; } 

迟到了,但是…

我创build了一个像@Daniel所提到的使用ModelMetadata的帮助器方法,我想我会分享它:

 public static string GetDisplayName<TModel, TProperty>( this TModel model , Expression<Func<TModel, TProperty>> expression) { return ModelMetadata.FromLambdaExpression<TModel, TProperty>( expression, new ViewDataDictionary<TModel>(model) ).DisplayName; } 

用法示例:

Models

 public class MySubObject { [DisplayName("Sub-Awesome!")] public string Sub { get; set; } } public class MyObject { [DisplayName("Awesome!")] public MySubObject Prop { get; set; } } 

Use

 HelperNamespace.GetDisplayName(Model, m => m.Prop) // "Awesome!" HelperNamespace.GetDisplayName(Model, m => m.Prop.Sub) // "Sub-Awesome!" 

只要这样做:

 using System.ComponentModel; using System.Linq; using System.Reflection; namespace yournamespace { public static class ExtensionMethods { public static string GetDisplayName(this PropertyInfo prop) { if (prop.CustomAttributes == null || prop.CustomAttributes.Count() == 0) return prop.Name; var displayNameAttribute = prop.CustomAttributes.Where(x => x.AttributeType == typeof(DisplayNameAttribute)).FirstOrDefault(); if (displayNameAttribute == null || displayNameAttribute.ConstructorArguments == null || displayNameAttribute.ConstructorArguments.Count == 0) return prop.Name; return displayNameAttribute.ConstructorArguments[0].Value.ToString() ?? prop.Name; } } } 

请求示例:

 var props = typeof(YourType).GetProperties(BindingFlags.Public | BindingFlags.Instance).Where(p => p.CanRead); var propFriendlyNames = props.Select(x => x.GetDisplayName()); 

我完全同意BuildStarted提供的解决scheme。 我只会改变的是,ExtensionsMethode不支持翻译。 为了支持这个,需要简单的小改动。 我会把这个意见,但我没有足够的要点这样做。 查找方法的最后一行。

扩展方法

 public static string GetDisplayName<TModel, TProperty>(this TModel model, Expression<Func<TModel, TProperty>> expression) { Type type = typeof(TModel); IEnumerable<string> propertyList; //unless it's a root property the expression NodeType will always be Convert switch (expression.Body.NodeType) { case ExpressionType.Convert: case ExpressionType.ConvertChecked: var ue = expression.Body as UnaryExpression; propertyList = (ue != null ? ue.Operand : null).ToString().Split(".".ToCharArray()).Skip(1); //don't use the root property break; default: propertyList = expression.Body.ToString().Split(".".ToCharArray()).Skip(1); break; } //the propert name is what we're after string propertyName = propertyList.Last(); //list of properties - the last property name string[] properties = propertyList.Take(propertyList.Count() - 1).ToArray(); Expression expr = null; foreach (string property in properties) { PropertyInfo propertyInfo = type.GetProperty(property); expr = Expression.Property(expr, type.GetProperty(property)); type = propertyInfo.PropertyType; } DisplayAttribute attr = (DisplayAttribute)type.GetProperty(propertyName).GetCustomAttributes(typeof(DisplayAttribute), true).SingleOrDefault(); // Look for [MetadataType] attribute in type hierarchy // http://stackoverflow.com/questions/1910532/attribute-isdefined-doesnt-see-attributes-applied-with-metadatatype-class if (attr == null) { MetadataTypeAttribute metadataType = (MetadataTypeAttribute)type.GetCustomAttributes(typeof(MetadataTypeAttribute), true).FirstOrDefault(); if (metadataType != null) { var property = metadataType.MetadataClassType.GetProperty(propertyName); if (property != null) { attr = (DisplayAttribute)property.GetCustomAttributes(typeof(DisplayNameAttribute), true).SingleOrDefault(); } } } //To support translations call attr.GetName() instead of attr.Name return (attr != null) ? attr.GetName() : String.Empty; } 

我做一个小的改变是你正在使用资源来获取DisplayName

  public static string GetDisplayName<TModel>(Expression<Func<TModel, object>> expression) { string _ReturnValue = string.Empty; Type type = typeof(TModel); string propertyName = null; string[] properties = null; IEnumerable<string> propertyList; //unless it's a root property the expression NodeType will always be Convert switch (expression.Body.NodeType) { case ExpressionType.Convert: case ExpressionType.ConvertChecked: var ue = expression.Body as UnaryExpression; propertyList = (ue != null ? ue.Operand : null).ToString().Split(".".ToCharArray()).Skip(1); //don't use the root property break; default: propertyList = expression.Body.ToString().Split(".".ToCharArray()).Skip(1); break; } //the propert name is what we're after propertyName = propertyList.Last(); //list of properties - the last property name properties = propertyList.Take(propertyList.Count() - 1).ToArray(); //grab all the parent properties Expression expr = null; foreach (string property in properties) { PropertyInfo propertyInfo = type.GetProperty(property); expr = Expression.Property(expr, type.GetProperty(property)); type = propertyInfo.PropertyType; } DisplayAttribute attr; attr = (DisplayAttribute)type.GetProperty(propertyName).GetCustomAttributes(typeof(DisplayAttribute), true).SingleOrDefault(); // Look for [MetadataType] attribute in type hierarchy // http://stackoverflow.com/questions/1910532/attribute-isdefined-doesnt-see-attributes-applied-with-metadatatype-class if (attr == null) { MetadataTypeAttribute metadataType = (MetadataTypeAttribute)type.GetCustomAttributes(typeof(MetadataTypeAttribute), true).FirstOrDefault(); if (metadataType != null) { var property = metadataType.MetadataClassType.GetProperty(propertyName); if (property != null) { attr = (DisplayAttribute)property.GetCustomAttributes(typeof(DisplayNameAttribute), true).SingleOrDefault(); } } } if (attr != null && attr.ResourceType != null) _ReturnValue = attr.ResourceType.GetProperty(attr.Name).GetValue(attr).ToString(); else if (attr != null) _ReturnValue = attr.Name; return _ReturnValue; } 

快乐的编码

我在这里发现了另一个不错的代码片段,我稍微修改了'DisplayName'的用途

  public static string GetDisplayName<TSource, TProperty>(Expression<Func<TSource, TProperty>> expression) { var attribute = Attribute.GetCustomAttribute(((MemberExpression)expression.Body).Member, typeof(DisplayNameAttribute)) as DisplayNameAttribute; if (attribute == null) { throw new ArgumentException($"Expression '{expression}' doesn't have DisplayAttribute"); } return attribute.DisplayName; } 

和用法

 GetDisplayName<ModelName, string>(i => i.PropertyName) 

代码.Net的另一个代码片段使用自己来执行此操作

 public static class WebModelExtensions { public static string GetDisplayName<TModel, TProperty>( this HtmlHelper<TModel> html, Expression<Func<TModel, TProperty>> expression) { // Taken from LabelExtensions var metadata = ModelMetadata.FromLambdaExpression(expression, html.ViewData); string displayName = metadata.DisplayName; if (displayName == null) { string propertyName = metadata.PropertyName; if (propertyName == null) { var htmlFieldName = ExpressionHelper.GetExpressionText(expression); displayName = ((IEnumerable<string>) htmlFieldName.Split('.')).Last<string>(); } else displayName = propertyName; } return displayName; } } // Usage Html.GetDisplayName(model => model.Password)