【问题标题】:Is this the correct way of creating and using a custom attribute on a method and a class?这是在方法和类上创建和使用自定义属性的正确方法吗?
【发布时间】:2013-01-04 01:07:47
【问题描述】:

今晚我一直在玩自定义属性,看看是否可以简化我的缓存层。我想出了以下几点:

namespace AttributeCreationTest
{
    [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, AllowMultiple = false)]
    public class Cache : Attribute
    {
        public Cache()
        {
            Length = "01h:30m";
        }

        public string Length;
    }

    [AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
    public class CacheIdentifier : Attribute
    {
    }


    [Cache]
    class Class1
    {
        [CacheIdentifier]
        public int ID { get; set; }
    }

    class Class2
    {
        [CacheIdentifier]
        public bool ID { get; set; }
    }

    [Cache(Length = "01h:10m")]
    class Class3
    {
        [CacheIdentifier]
        public string ID { get; set; }
    }

    class Program
    {

        static void Main(string[] args)
        {
            var f1 = new Class1 { ID = 2 };
            var f2 = new Class2 { ID = false };
            var f3 = new Class3 { ID = "someID" };

            DoCache(f1);
            DoCache(f2);
            DoCache(f3);
        }

        public static void DoCache(object objectToCache)
        {
            var t = objectToCache.GetType();

            var attr = Attribute.GetCustomAttribute(t, typeof(Cache));

            if (attr == null) return;

            var a = (Cache)attr;
            TimeSpan span;

            if (TimeSpan.TryParse(a.Length.Replace("m", "").Replace("h", ""), out span))
            {
                Console.WriteLine("name: {0}, {1}", t.Name, span);

                ExtractCacheData(objectToCache);

                return;
            }

            throw new Exception(string.Format("The Length value of {0} for the class {1} is invalid.", a.Length, t.Name)); 
        }

        public static void ExtractCacheData(object o)
        {
            var t = o.GetType();

            foreach (var prop in t.GetProperties(BindingFlags.Instance | BindingFlags.Public))
            {
                if (Attribute.IsDefined(prop, typeof(CacheIdentifier)))
                {
                    Console.WriteLine("   type: {0}, value {1}", prop.PropertyType, prop.GetValue(o));
                    break;
                }

                throw new Exception(string.Format("A CacheIdentifier attribute has not been defined for {0}.", t.Name));
            }
        }

    }
}

“缓存”属性将被充实,但我在学习 C# 的这一领域时将其保留在最低限度。我的想法是允许更轻松地缓存项目,包括指定缓存对象的时间量的简化方法。

这看起来好吗?使用这种模式将项目推送到缓存是否会对性能产生重大影响?

我找不到任何详细介绍这种想法的教程,因此我们将不胜感激。

【问题讨论】:

    标签: c# .net custom-attributes


    【解决方案1】:

    您的实现似乎很好,但CacheAttribute 对象将是每个属性声明一个,而不是每个Class1 对象。换句话说,无论您将创建多少对象——如果属性只分配一次——具有Length 属性的该属性的对象将只是clr 中的一个。

    但是你为什么不想将一些接口传递给DoCache 方法,你将定义一些方法来从对象中检索 CacheIdentifier 呢?这样的实现将更加健壮、准确、可读。当然,您需要在每个类中实现它 - 但我看不到将object 传递给DoCache 而不指定其任何职责有很多好处。对于用户而言,此 API 不直观且难以理解。

    另一点,如果你愿意的话,更高级 - 出于缓存目的,我建议总体上看面向方面的编程,特别是 PostSharp。这篇文章5 Ways That Postsharp Can SOLIDify Your Code: Caching 为实现缓存方面提供了一个很好的案例。简而言之,PostSharp 还在编译的第二步通过 IL weaving 使用属性和插入缓存行为

    编辑:如果你想要漂亮和干净的模型,没有任何非域元素的扭曲,我建议另一种 AOP 技术,称为动态拦截。最流行的框架是LinFuCastle Dynamic Proxy。结果,您将通过构造函数指定依赖项,并且 IoC 容器将在解析这些依赖项时创建代理。但关键是这些模型对代理一无所知,并将在简单对象中使用它们。这有点类似于Decorator pattern

    【讨论】:

    • 谢谢莉亚!我以前从未遇到过 PostSharp,但我肯定会检查一下。我考虑使用接口,但我接手的项目遵循 DDD,我不确定其他程序员是否会过于热衷于向域对象(要缓存的类)添加非域特定信息 - 这是我不过会提出来!
    • @ChrisW 是的,但属性也是依赖项,也是非域特定信息。这就是为什么 Robert Martin 批评了依赖注入的属性,例如 Inject
    • 那是真的 - 就在我发布它之后,它击中了我,但你在我设法编辑我的帖子之前回复了:)。我将与其他开发人员讨论使用哪种方法 - 无论选择哪种方法,最好有属性创建方面的背景。
    • 我建议看一种 AOP 技术,称为动态拦截。最流行的框架是LinFuCastle Dynamic Proxy。此技术不需要模型上存在的非域特定元素的任何信息。如果通过构造函数正确使用依赖注入 - 你将拥有漂亮而干净的域模型。
    【解决方案2】:

    您的用法基本正确,但您可能希望缓存结果。您可以将类型传递给它,而不是传递 object 的实例。此外,为了避免每次都使用反射——这肯定不会加快程序的执行——你可以将类型及其相关属性保存在字典中,并在每次使用该方法时进行查找被调用,然后只有在查找失败时才通过反射操作。

    另外,不是强制性的,但我建议您关注naming convention 获取属性。

    保持结果缓存的示例:

    static readonly Dictionary<Type, Attribute> CacheAttributes = 
        new Dictionary<Type,Attribute>();
    

    那么你将修改DoCache如下:

    var t = objectToCache.GetType();
    Attribute attr;
    var success = CacheAttributes.TryGetValue(t, out attr);
    if (!success)
    {
        attr = Attribute.GetCustomAttribute(t, typeof (CacheAttribute));
        CacheAttributes[t] = attr;
    }
    if (attr == null) return;
    var a = attr as CacheAttribute;
    TimeSpan span;
    //Continues with your code
    

    您可以将相同的概念应用于ExtractCacheData

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-01-19
      • 2013-04-25
      • 2022-10-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-02-14
      • 1970-01-01
      相关资源
      最近更新 更多