假设我们有一个名为“ Planet”的枚举,并且它具有类“ PlanetAttr”的自定义属性,
这些方法将为您提供给定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
}


评论

我很好奇您为什么决定使用元注释来表示此数据?似乎是一个奇怪的选择-属性被设计为存储元数据(关于数据的数据),但是在这种情况下,您存储的是实体的属性-面向对象的编程就是这样!这些额外的属性是必需的,这意味着枚举不是要使用的适当数据类型。.

@MattDavey很公平,它是一个人为的示例和一个字典或数组,可能是将枚举链接到对象的更好方法。枚举仍然是适当的-它代表行星的名称,而不是行星的属性(在单独的类中)。

@MattDavey:“元”是一个观点问题。一个人的数据可以是另一个人的元数据,反之亦然。您可能会认为数据库表定义是LOB应用程序的元数据,但它是ORM设计人员的数据。我以一种非常简单的方式画了一条线-我认为源代码中写入的任何数据都是元数据(不包括示例数据)。行星枚举不是使用元数据的光辉典范,而是继续–在一个通过对行星进行命名来管理服务器的程序中,有关实际天文物体的任何其他信息都是元数据。

@finnw:词典确实是元数据的合适运行时容器,而不是编译时容器。可以在程序启动时或需要时(延迟加载)从属性中包含的元数据构造和填充字典。

#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