从内部类获取 DisplayNameAttribute
我有一个被声明为内部的类。它用各种注释进行装饰。特别是 [DisplayName("My Display Name")] 注释。我有一些代码可以检索该值,但仅在该类被声明为公共时才有效。我对使用反射有点陌生。我相信我需要指定使用 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
Debug.WriteLine(item.DisplayName(i => i.FName));
I have a class that is declared Internal. It is decorated with various annotations. In particular is the [DisplayName("My Display Name")] annotation. I have some code that will retrieve the value but only works if the class is declared public. I am sort of new to using reflection. I believe I need to specify that the BindingFlags.NonPublic be used but I am not sure where.
LinqPAD code:
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;
}
}
Usage:
If you don't have LinqPAD, you should download it then you can test this pretty easily by just creating a new C# Program in LinkPAD
Debug.WriteLine(item.DisplayName(i => i.FName));
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
因此,您似乎希望能够通过在单独的部分片段中提供元数据来装饰部分类的现有成员。没有内置机制(参见例如 这个问题和类答案中提到),但如果您愿意遵守约定,您可以推出自己的约定:
因此,假设我们有
一部分无法更改,并且
在部分片段中,我们可以改变。您已经掌握了大部分内容:在
DisplayName()
中,您成功确定我们正在查看FName
属性;然后,您在T.FName
上查找DisplayNameAttribute
,但没有找到,所以就到此为止。您需要做的是,如果找不到所需的属性,
请查找名为
Metadata
的嵌套类 - 请注意,这里有一个地方我们使用BindingFlags.NonPublic< /code>
如果我们找到一个:
查找与最初讨论的同名的成员(再次
BindingFlags.NonPublic
)如果有一个,请使用您的辅助方法,但这次通过它是元数据类型的成员:(
我在这里省略了最终的无效检查,以及关闭控制流)
根据您对
"Metadata"
字符串的厌恶程度,您可以做一些声明性的事情带属性:SpGetProfileInfoResult
(您可以更改的部分)上,该属性使用指向其
(这是 System. ComponentModel),或者元数据
类型Metadata
上的类级属性,让它声明“我是元数据类型”。然后,我们不再搜索名为固定字符串的嵌套类,而是搜索具有此特定属性的嵌套类。So it looks like you want to be able to decorate existing members of a partial class, by providing metadata in a separate partial piece. There's no built-in mechanism for that (see eg this question and the classes mentioned in the answer), but if you're willing to stick to a convention, you can roll your own:
So suppose we have
in a partial piece we can't change, and
in a partial piece we can change. You already have most of the pieces: in
DisplayName()
, you successfully determine that we are looking at theFName
property; you then look for aDisplayNameAttribute
onT.FName
, but there isn't one, so that's where it stops.What you need to do is, in the case where you don't find the attribute you need,
Look for a nested class named
Metadata
- note here is one place we useBindingFlags.NonPublic
If we find one:
Look for a member of the same name as was originally being talked about (
BindingFlags.NonPublic
again)If there is one, use your helper method, but this time pass it the metadata type's member:
(I've omitted a final nullity check here, as well as closing the control flow)
Depending on how distasteful you find that
"Metadata"
string, you could instead do something declarative with attributes:SpGetProfileInfoResult
(the piece you can change) that points at itsMetadata
usingtypeof
(this is the approach taken bySystem.ComponentModel
), orMetadata
, to have it claim 'I am a metadata type'. Then instead of searching for a nested class named a fixed string, we would instead search for a nested class having this particular attribute.经过一段时间的研究后,我想出了一个 Hack。我确信有人可以帮助我清理一下这个问题,但这就是我发现的有效方法。我必须将“元数据”嵌套类添加到 DeclaringType,然后对该结果执行 GetMember,这会返回成员集合。
After working on this for a while I came up with a Hack. I am sure someone out there can help me clean this up a bit, but this is what I found works. I had to add the "Metadata" nested class to the DeclaringType and then do a GetMember on that result, which returns a collection of members.
我不会尝试调试您的代码,因为您使用了一些我不熟悉的类。
我确实知道的一件事是 MemberInfo 没有
GetAttribute() 函数。您必须在那里使用扩展方法。
不过我可以告诉您,您不需要任何特殊的绑定标志,只是因为类型是
内部
。只有成员的可见性很重要,在本例中它是公开的。输出:
I won't try to debug your code because you're using some classes that I'm not familiar with.
One thing I do know is that MemberInfo does not have a
GetAttribute()
function. You must be using an extension method there.However I can tell you that you don't need any special bindingflags just because the type is
internal
. Only the visibility of the member is important, and in this case it's public.Output: