Update: @JeremyDWill rightly pointed out that you can't derive a generic type from one of its parameters...
If you think of the decorator as a "subclass that adds new properties", you could do something like:
public class MyDecorator<T> : T
{
public int MyDecoratorProperty1 { get; set; }
public int MyDecoratorProperty2 { get; set; }
}
Then you can create instances of MyDecorator<DataBag>
, and MyDecorator<OtherClass>
, etc. The existing properties are accessible because the MyDecorator<>
is specific to the type of the generic argument, and derives from that class.
You can create a wrapper which contains the decorated object:
public class MyDecorator<T>
{
public MyDecorator(T decoratedObject)
{
this.DecoratedObject = decoratedObject;
}
public T DecoratedObject { get; private set; }
public int MyDecoratorProperty1 { get; set; }
public int MyDecoratorProperty2 { get; set; }
}
The advantage is that getting to the decorated properties is easy: myObj.MyDecoratorProperty1
. The downside is that you now have to go through the DecoratedObject
member to get to the base object:
DataBag bag = new DataBag("", null, null);
MyDecorator<DataBag> deco = new MyDecorator<DataBag>(bag);
deco.DecoratedObject.Height = 2;
If you can't subclass from the decorate (you need to support multiple decorators at a time, say), you'll have to do something like an "attached property"... your decorator class would have to keep a dictionary of original objects and the decorated properties. With a few extension methods, you could make these properties "look" like native members of the decorated class as long as you know the types getting decorated in advance (or are willing to decorate any object):
public static class AttachedDecorator
{
private class Properties
{
public int MyDecoratorProperty1 { get; set; }
public int MyDecoratorProperty2 { get; set; }
}
private static Dictionary<object, Properties> map = new Dictionary<object, Properties>();
public static int GetMyDecoratorProperty1(object obj)
{
Properties props;
if (map.TryGetValue(obj, out props))
{
return props.MyDecoratorProperty1;
}
return -1; // or some value that makes sense if the object has no decorated property set
}
public static int GetMyDecoratorProperty2(object obj) { /* ... */ }
public static void SetMyDecoratorProperty1(object obj, int value)
{
Properties props;
if (!map.TryGetValue(obj, out props))
{
props = new Properties();
map.Add(obj, props);
}
props.MyDecoratorProperty1 = value;
}
public static void SetMyDecoratorProperty2(object obj, int value) { /* ... */ }
}
public static class DecoratorExtensions
{
private static int GetMyDecoratorProperty1(this object obj)
{
return AttachedDecorator.GetMyDecoratorProperty1(obj);
}
private static void SetMyDecoratorProperty1(this object obj, int value)
{
return AttachedDecorator.GetMyDecoratorProperty1(obj, value);
}
// ...
}
Your code then might look like:
DataBag myData = new DataBag();
myData.SetMyDecoratorProperty1(7);
Console.WriteLine("prop1: {0}", myData.GetMyDecoratorProperty1());