这些方法将为您提供给定Planet值的属性值:
private static PlanetAttr GetAttr(Planet p)
{
return (PlanetAttr)Attribute.GetCustomAttribute(ForValue(p), typeof(PlanetAttr));
}
private static MemberInfo ForValue(Planet p)
{
return typeof(Planet).GetField(Enum.GetName(typeof(Planet), p));
}
但这是从枚举中检索自定义属性的值的好方法吗?我对涉及的方法调用数量感到不舒服。有没有更有效的方法,例如
这段代码来自StackOverflow问题。
using System;
using System.Reflection;
class PlanetAttr: Attribute
{
internal PlanetAttr(double mass, double radius)
{
this.Mass = mass;
this.Radius = radius;
}
public double Mass { get; private set; }
public double Radius { get; private set; }
}
public static class Planets
{
public static double GetSurfaceGravity(this Planet p)
{
PlanetAttr attr = GetAttr(p);
return G * attr.Mass / (attr.Radius * attr.Radius);
}
public static double GetSurfaceWeight(this Planet p, double otherMass)
{
return otherMass * p.GetSurfaceGravity();
}
public const double G = 6.67300E-11;
private static PlanetAttr GetAttr(Planet p)
{
return (PlanetAttr)Attribute.GetCustomAttribute(ForValue(p), typeof(PlanetAttr));
}
private static MemberInfo ForValue(Planet p)
{
return typeof(Planet).GetField(Enum.GetName(typeof(Planet), p));
}
}
public enum Planet
{
[PlanetAttr(3.303e+23, 2.4397e6)] MERCURY,
[PlanetAttr(4.869e+24, 6.0518e6)] VENUS,
[PlanetAttr(5.976e+24, 6.37814e6)] EARTH,
[PlanetAttr(6.421e+23, 3.3972e6)] MARS,
[PlanetAttr(1.9e+27, 7.1492e7)] JUPITER,
[PlanetAttr(5.688e+26, 6.0268e7)] SATURN,
[PlanetAttr(8.686e+25, 2.5559e7)] URANUS,
[PlanetAttr(1.024e+26, 2.4746e7)] NEPTUNE,
[PlanetAttr(1.27e+22, 1.137e6)] PLUTO
}
#1 楼
使用泛型使其通用。尽管我强烈建议您重命名属性以遵循.NET准则。它应始终采用
AttributeNameAttribute
的形式。枚举值应在CamelCase中。通常,将包含扩展方法的类命名为与其扩展的类相同的名称,后跟Extensions
。否则,这就是您将如何获取字段的自定义属性的原因。<应当指出,从.Net 4.5开始,框架中现在有一些检索自定义属性的方法。请参见CustomAttributeExtensions类。这应该简化扩展方法。最好将扩展名更改为
GetCustomAttribute()
,因为它始终是自定义属性。public static class EnumExtensions
{
public static TAttribute GetAttribute<TAttribute>(this Enum value)
where TAttribute : Attribute
{
var type = value.GetType();
var name = Enum.GetName(type, value);
return type.GetField(name) // I prefer to get attributes this way
.GetCustomAttributes(false)
.OfType<TAttribute>()
.SingleOrDefault();
}
}
public class PlanetInfoAttribute : Attribute
{
internal PlanetInfoAttribute(double mass, double radius)
{
this.Mass = mass;
this.Radius = radius;
}
public double Mass { get; private set; }
public double Radius { get; private set; }
}
public enum Planet
{
[PlanetInfo(3.303e+23, 2.43970e6)] Mecury,
[PlanetInfo(4.869e+24, 6.05180e6)] Venus,
[PlanetInfo(5.976e+24, 6.37814e6)] Earth,
[PlanetInfo(6.421e+23, 3.39720e6)] Mars,
[PlanetInfo(1.900e+27, 7.14920e7)] Jupiter,
[PlanetInfo(5.688e+26, 6.02680e7)] Saturn,
[PlanetInfo(8.686e+25, 2.55590e7)] Uranus,
[PlanetInfo(1.024e+26, 2.47460e7)] Neptune,
[PlanetInfo(1.270e+22, 1.13700e6)] Pluto,
}
public static class PlanetExtensions
{
public static double GetSurfaceGravity(this Planet p)
{
var attr = p.GetAttribute<PlanetInfoAttribute>();
return G * attr.Mass / (attr.Radius * attr.Radius);
}
public static double GetSurfaceWeight(this Planet p, double otherMass)
{
return otherMass * p.GetSurfaceGravity();
}
public const double G = 6.67300E-11;
}
评论
\ $ \ begingroup \ $
杜德,这真棒!我只是创建一个帐户来支持此目的。我已经为我重写了它,因此它可以用于枚举。所以我可以将枚举值与枚举关联。太棒了,认真! :D
\ $ \ endgroup \ $
– C4d
16年8月18日在11:53
\ $ \ begingroup \ $
你能写你的重写代码吗
\ $ \ endgroup \ $
–HüseyinSekmenoğlu
19年1月9日,12:57
\ $ \ begingroup \ $
同样,只要加入社区就可以投票给您
\ $ \ endgroup \ $
–马吉德·哈利利
2月23日在1:24
评论
我很好奇您为什么决定使用元注释来表示此数据?似乎是一个奇怪的选择-属性被设计为存储元数据(关于数据的数据),但是在这种情况下,您存储的是实体的属性-面向对象的编程就是这样!这些额外的属性是必需的,这意味着枚举不是要使用的适当数据类型。.@MattDavey很公平,它是一个人为的示例和一个字典或数组,可能是将枚举链接到对象的更好方法。枚举仍然是适当的-它代表行星的名称,而不是行星的属性(在单独的类中)。
@MattDavey:“元”是一个观点问题。一个人的数据可以是另一个人的元数据,反之亦然。您可能会认为数据库表定义是LOB应用程序的元数据,但它是ORM设计人员的数据。我以一种非常简单的方式画了一条线-我认为源代码中写入的任何数据都是元数据(不包括示例数据)。行星枚举不是使用元数据的光辉典范,而是继续–在一个通过对行星进行命名来管理服务器的程序中,有关实际天文物体的任何其他信息都是元数据。
@finnw:词典确实是元数据的合适运行时容器,而不是编译时容器。可以在程序启动时或需要时(延迟加载)从属性中包含的元数据构造和填充字典。