2013-01-04 48 views
3

我一直在玩弄自定義屬性今天晚上,看看我是否可以簡化我的緩存層。我想出了以下內容:這是在方法和類上創建和使用自定義屬性的正確方法嗎?

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#這個區域。我的想法是允許更容易緩存項目,包括指定緩存對象的時間量的簡化方法。

這看起來好嗎?使用這種模式將項目緩存到緩存中會有什麼顯着的性能命中嗎?

我一直無法找到任何詳細的涵蓋這種想法的任何教程,所以任何建議,將不勝感激。

回答

2

你的實現似乎很好,但CacheAttribute對象將是每個屬性聲明一個,而不是每個Class1對象。換句話說,無論您要創建多少個對象 - 如果屬性只會分配一次 - 該屬性的對象Length屬性將只在clr之一中。

但是,爲什麼你不想傳入DoCache方法的某個接口,你將定義一些方法從對象中檢索CacheIdentifier?這樣的實現將更加健壯,精確,可讀。當然,您需要在每個班級中實施 - 但我沒有看到將object轉換成DoCache而沒有指定任何責任的好處。對於用戶來說,這個API不直觀且難以理解。

另一點,如果你願意,可以採用更高級的方式 - 爲了緩存目的,我建議一般來看看面向方面編程,特別是PostSharp。這篇文章5 Ways That Postsharp Can SOLIDify Your Code: Caching爲實現緩存方面提供了一個很好的例子。簡單地說,PostSharp還使用屬性和鑲在通過IL編譯第二步緩存行爲編織

編輯:如果不想非域元素的任何畸變非常乾淨的模型,我建議另一種AOP技術,被稱爲動態攔截。最流行的框架是LinFuCastle Dynamic Proxy。因此,在解決這些依賴關係時,您將通過構造函數指定您的依賴項,並且IoC容器將創建代理。但重點在於那些模型對這個代理將一無所知,並將它們用作簡單對象的地方。這有些類似於Decorator pattern

+0

謝謝llya!我以前從未遇到過PostSharp,但我一定會查看它。我考慮使用一個接口,但我接管的項目遵循DDD,我不確定其他程序員會過分熱衷於向域對象(要緩存的類)添加非域特定信息 - 這是我儘管如此! –

+1

@ChrisW是的,但屬性也是一個依賴項,也是一個非域特定的信息。這就是爲什麼羅伯特馬丁批評依賴注入的屬性如「注入」。 –

+0

這是非常真實的 - 就在我發佈它後,打我,但你回答之前,我設法編輯我的帖子:)。我會與其他開發人員討論使用哪種方法 - 無論選擇哪種方法,最好在屬性創建時使用此背景。 –

2

您的使用情況基本正確,但您可能需要緩存結果。您可以將類型傳遞給它,而不是傳遞object的實例。此外,爲了避免每次都使用Reflection - 這並不會加快程序的執行速度 - 您可以將類型及其相關屬性保存在字典中,並在每次使用該方法時進行查找被調用,然後只有在查找失敗時才通過Reflection進行操作。

此外,不是強制性的,但我建議您按照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

相關問題