I’ve been playing around with custom attributes this evening to see if I could simplify my caching layer. I have come up with the following:
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));
}
}
}
}
The “Cache” attribute will be fleshed out, but I’ve left it minimal whilst learning this area of C#. My idea was to allow easier caching of items, including a simplified method of specifying amount of time to cache an object.
Does this look ok? Would there be any significant performance hits from using this kind of pattern to push items to cache?
I haven’t been able to find any tutorials which cover this kind of idea in any detail, so any advice would be appreciated.
Your implementation seems to be fine, but
CacheAttributeobject will be one per attribute declaration, not perClass1object. In other words it doesn’t matter how many objects you will create – if attribute will be assigned only once – the object of that attribute withLengthproperty will be only one inclr.But why don’t you want to pass into
DoCachemethod some interface, which you will define some method to retrieve a CacheIdentifier from an object? Such implementation will be more robust, exact, readable. Surely you need to implement it in each class – but I don’t see lots of benefits in passingobjectintoDoCachewithout specifying any of its responsibilities. For user this API is not intuitive and hard to understand.Another point, more advanced if you will – for caching purposes I suggest to look at Aspect-Oriented Programming in general and at PostSharp in particular. This article 5 Ways That Postsharp Can SOLIDify Your Code: Caching makes a great case for implementing caching aspects. Briefly, PostSharp also uses attributes and inserts caching behavior at second step of compilation via IL weaving
EDIT: If you want nice and clean model without any distortions of non-domain elements, I suggest another AOP technique, known as dynamic interception. Most popular frameworks are LinFu and Castle Dynamic Proxy. As result, you will have your dependencies specified via constructor and IoC container will create proxies, when resolving these dependencies. But the point is that those models will know nothing about that proxies and will use them as there where simple objects. This is somewhat similar to the Decorator pattern