从内部类获取DisplayNameAttribute



我有一个声明为Internal的类。它装饰着各种各样的注释。特别是[DisplayName("我的显示名称")]注释。我有一些代码可以检索值,但只有当类被声明为公共时才有效。我对使用反思有点陌生。我认为我需要指定使用BindingFlags.NonPublic,但我不确定在哪里使用。

LinqPAD代码:

void Main()
{
    List<SpGetProfileInfoResult> p = new List<SpGetProfileInfoResult>();
    p.Add(new SpGetProfileInfoResult() { FName = "Eric" });
    p.Add(new SpGetProfileInfoResult() { FName = "Mike" });
    p.Dump();
    foreach (var item in p)
    {
        Console.WriteLine(item.DisplayName(i => i.FName));
        Console.WriteLine(item.FName);
    }
}
public partial class SpGetProfileInfoResult
{
    // Uncomment this annotation to see that this part will work
    // [System.ComponentModel.DisplayNameAttribute("[BILLTO-FNAME]")]
    public string FName { get; set; }
}
public partial class SpGetProfileInfoResult
{
    internal class Metadata
    {
        // This attribute is never available seems.
        [System.ComponentModel.DisplayNameAttribute("[BILL-FNAME]")]
        public string FName { get; set; }
    }
}
public static class Tag
{
    public static T GetAttribute<T>(this MemberInfo member, bool isRequired) where T : Attribute
    {
        var attribute = member.GetCustomAttributes(typeof(T), false).SingleOrDefault();
        if (attribute == null && isRequired)
        {
            throw new ArgumentException(
                string.Format(
                "The {0} attribute must be defined on member {1}",
                typeof(T).Name,
                member.Name));
        }
        return (T)attribute;
    }
    public static string DisplayName<T>(this T src,Expression<Func<T, object>> propertyExpression)
    {
        Type metadata = null;
        var memberInfo = GetPropertyInformation(propertyExpression.Body);
        if (memberInfo == null)
        {
            throw new ArgumentException(
                "No property reference expression was found.",
                "propertyExpression");
        }
        var attr = memberInfo.GetAttribute<DisplayNameAttribute>(false);
        if (attr == null)
        {
            return memberInfo.Name;
        }
        return attr.DisplayName;
    }
    public static MemberInfo GetPropertyInformation(Expression propertyExpression)
    {
        MemberExpression memberExpr = propertyExpression as MemberExpression;
        if (memberExpr == null)
        {
            UnaryExpression unaryExpr = propertyExpression as UnaryExpression;
            if (unaryExpr != null && unaryExpr.NodeType == ExpressionType.Convert)
            {
                memberExpr = unaryExpr.Operand as MemberExpression;
            }
        }
        if (memberExpr != null && memberExpr.Member.MemberType == MemberTypes.Property)
        {
            return memberExpr.Member;
        }
        return null;
    }
}

用法:

如果你没有LinqPAD,你应该下载它,然后你可以很容易地通过在LinkPAD 中创建一个新的C#程序来测试它

Debug.WriteLine(item.DisplayName(i => i.FName));

因此,您似乎希望能够通过在单独的分部中提供元数据来装饰分部类的现有成员。没有内置的机制(例如,参见这个问题和答案中提到的类),但如果你愿意遵守惯例,你可以推出自己的:

所以假设我们有

public partial class SpGetProfileInfoResult
{
    public string FName { get; set; }
}

在局部片段中,我们不能更改,并且

public partial class SpGetProfileInfoResult
{
    internal class Metadata
    {
        [System.ComponentModel.DisplayNameAttribute("[BILL-FNAME]")]
        public string FName { get; set; }
    }
}

在一个分部中,我们可以改变。您已经拥有了大部分内容:在DisplayName()中,您成功地确定我们正在查看FName属性;然后在T.FName上查找DisplayNameAttribute,但没有,所以它停止了。

你需要做的是,在你找不到你需要的属性的情况下,

var attr = memberInfo.GetAttribute<DisplayNameAttribute>(false);
if (attr == null)
{

寻找一个名为Metadata的嵌套类-注意这里是我们使用BindingFlags.NonPublic 的一个地方

    // Try and get a nested metadata class
    var metadataType = typeof(T)
        .GetNestedType("Metadata", 
                       BindingFlags.Public | BindingFlags.NonPublic);

如果我们找到一个:

    if (metadataType != null)
    {

寻找与最初讨论的名称相同的成员(再次为BindingFlags.NonPublic

        var membersOnMetadataType = metadataType.GetMember(memberInfo.Name, 
            BindingFlags.Instance |
            BindingFlags.Public | 
            BindingFlags.NonPublic);

如果有,请使用helper方法,但这次将元数据类型的成员传递给它:

        if (membersOnMetadataType.Any())
        {
            var attrOnMetadataType = membersOnMetadataType[0]
                .GetAttribute<DisplayNameAttribute>(false);
            return attrOnMetadataType.DisplayName;

(我在这里省略了最后的无效性检查,以及关闭控制流)

根据你对"Metadata"字符串的反感程度,你可以用属性做一些声明性的事情:

  • 具有SpGetProfileInfoResult(您可以更改的工件)上的类级属性,该属性使用typeof指向其Metadata(这是System.ComponentModel所采用的方法),或者
  • 具有Metadata上的类级属性,以使其声明"我是元数据类型"。然后,我们不搜索名为固定字符串的嵌套类,而是搜索具有此特定属性的嵌套类

在做了一段时间之后,我想出了一个黑客。我相信有人可以帮我清理一下,但这就是我发现的有效方法。我必须将"Metadata"嵌套类添加到DeclaringType中,然后对该结果执行GetMember,返回成员的集合。

public static string DisplayName<T>(this T src, Expression<Func<T, object>> propertyExpression)
{
    var memberInfo = GetPropertyInformation(propertyExpression.Body);
    var mytype = src.GetType();
    string strType = mytype.Name + "+Metadata";
    var metaType = Type.GetType(strType);
    MemberInfo[] mem = metaType.GetMember(memberInfo.Name);
    var att = mem[0].GetCustomAttributes(typeof(DisplayNameAttribute), true).FirstOrDefault() as DisplayNameAttribute;
    if (att == null)
        return memberInfo.Name;
    else
        return att.DisplayName;
}

我不会尝试调试您的代码,因为您使用的是一些我不熟悉的类。

我知道的一件事是MemberInfo没有GetAttribute()函数。您必须在那里使用扩展方法。

但是,我可以告诉你,你不需要任何特殊的绑定标志,因为类型是internal。只有成员的可见性才是重要的,在这种情况下,它是公开的。

using System;
using System.ComponentModel;
namespace ConsoleApplication1
{
    internal class Metadata
    {
        [DisplayName("[BILL-FNAME]")]
        public string FName { get; set; }
    } 
    class Program
    {
        static void Main()
        {
            var memberInfo = typeof(Metadata).GetMember("FName")[0];
            var atrributes = memberInfo.GetCustomAttributes(false);
            Console.WriteLine(atrributes[0].GetType().Name);
        }
    }
}

输出:

DisplayName属性

相关内容

  • 没有找到相关文章

最新更新