C# 从内部类获取DisplayNameAttribute

C# 从内部类获取DisplayNameAttribute,c#,.net,reflection,C#,.net,Reflection,我有一个声明为内部的类。它用各种注释装饰。特别是[DisplayName(“我的显示名”)]注释。我有一些代码将检索该值,但只有当该类声明为公共时才有效。我对使用反射有点陌生。我认为我需要指定BindingFlags.NonPublic可以使用,但我不确定在哪里 LinqPAD代码: void Main() { List<SpGetProfileInfoResult> p = new List<SpGetProfileInfoResult>(); p.Ad

我有一个声明为内部的类。它用各种注释装饰。特别是[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;
    }
}

我不会调试您的代码,因为您正在使用一些我不熟悉的类

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

但是,我可以告诉您,您不需要任何特殊的bindingflags,因为类型是
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);
        }
    }
}
输出:

DisplayNameAttribute


因此,看起来您希望能够通过在单独的分部中提供元数据来修饰分部类的现有成员。这方面没有内置的机制(参见示例),但如果您愿意遵守约定,您可以推出自己的:

那么假设我们有

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)
{
如果我们找到一个:

    if (metadataType != null)
    {
查找与最初讨论的名称相同的成员(
BindingFlags.NonPublic

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

        if (membersOnMetadataType.Any())
        {
            var attrOnMetadataType = membersOnMetadataType[0]
                .GetAttribute<DisplayNameAttribute>(false);
            return attrOnMetadataType.DisplayName;
if(membersOnMetadataType.Any())
{
var attrOnMetadataType=membersOnMetadataType[0]
.GetAttribute(false);
返回attrOnMetadataType.DisplayName;
(我在这里省略了最后的空性检查,并关闭了控制流)

根据您对
“元数据”
字符串的厌恶程度,您可以使用属性执行声明性操作:

  • 使用
    typeof
    (这是)指向其
    元数据的
    类级属性(可以更改的部分),或者
  • 有一个class-level属性,该属性位于
    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;
}
publicstaticstringdisplayname(此T src,expressionpropertyexpression)
{
var memberInfo=GetPropertyInformation(propertyExpression.Body);
var mytype=src.GetType();
字符串strType=mytype.Name+“+Metadata”;
var metaType=Type.GetType(strType);
MemberInfo[]mem=metaType.GetMember(MemberInfo.Name);
var att=mem[0]。GetCustomAttributes(typeof(DisplayNameAttribute),true)。FirstOrDefault()作为DisplayNameAttribute;
如果(att==null)
返回memberInfo.Name;
其他的
返回att.DisplayName;
}

我更新了我的示例。您对GetAttribute的看法是正确的。我添加了函数,以便整个示例都清晰明了。我还更新了类,以显示工具如何生成类。由于元数据是内部的,因此我无法以您所示的方式访问它。我尝试时会起作用。尝试时会出现什么问题?另外,这对t您已经包含了代码,但是一个最小的编译示例将非常有用。有两个分部类,一个具有许多属性,即生成的类,然后是上面的一个,即元数据类。我只能访问生成的类的属性,而不能访问任何内部类。该项目有点庞大,因此我在我无法创建这样一个小的示例。我试图将其分解为最基本的组件。是的,我知道这很棘手。但是考虑到
内部
公共
之间在行为上存在差异,我们真的需要看看程序集的分界线在哪里。就目前情况而言,您正在进行的唯一反射调用是de>GetCustomAttributes
one,它没有任何
BindingFlags
参数。使用新的示例更新了它…这一个非常简洁,您可以取消注释working属性以查看差异。现在我不确定您希望发生什么。您的
GetAttribute
方法正在查找它的成员上的属性传递,这是
SpGetProfileInfoResult
上的
FName
属性。您不是指
SpGetProfileInfoResult.Metadata
(虽然是嵌套的,但它是另一个类)任何地方,所以我不确定您希望何时查看它。
DisplayName
中的
元数据
变量未在当前代码中使用-还有更多内容要看吗?经过深思熟虑的答案。可能比我的解决方案更优雅一些,但看起来我们得出了相同的答案。
        var membersOnMetadataType = metadataType.GetMember(memberInfo.Name, 
            BindingFlags.Instance |
            BindingFlags.Public | 
            BindingFlags.NonPublic);
        if (membersOnMetadataType.Any())
        {
            var attrOnMetadataType = membersOnMetadataType[0]
                .GetAttribute<DisplayNameAttribute>(false);
            return attrOnMetadataType.DisplayName;
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;
}