tags:

views:

158

answers:

2

A co-worker of mine asked me last week if it were possible in C# to extend a generic class from its generic parameter. He said it was possible in C++. What he wanted makes actually sense. He wanted a generic decorator to annotate an arbitrary class with additional information. Something like:

pubic class Decorator<T> : T
{
  public object AdditionalInformation {get:set;}
}

So that he can now use this generic decorator everywhere instead of T.

The most similar thing I could come with was a container class with the original object, the additional information and an implicit conversion.

public class Decorator<T>
{
    private readonly T _instance;
    public Decorator(T instance)
    {
        _instance = instance;
    }

    public T Instance
    {
        get { return _instance; }
    }
    public object AdditionalInformation { get; set; }

    public static implicit operator T(Decorator<T> deco)
    {
        return deco._instance;
    }
}

But this is not the same because the implicit conversion is only one way. He cannot use it, for example, as a return type of a method because the additional information would be lost after he implicit conversion.

Does anybody have a better idea?

+1  A: 

If you can derive all decorable classes from some base class, then you can try to store decorators in that base class and make its info recoverable. Here is sample code that is guaranteed to contain some errors, but you can get the idea.

public class Decorable
{
    Dictionary<Type,object> decors = new Dictionary<Type,object>();
    public void AddDecorator<D>(D decor) { decors[typeof(D)] = decor; }
    public D GetDecorator<D>()
    {
        object value;
        if (decors.TryGetValue(typeof(D), out value))
            return (D)value;
        else
            return default(D);
    }

}

public class Decorator<T> where T: class, Decorable
{
    private readonly T _instance;
    public Decorator(T instance)
    {
        _instance = instance;
        instance.AddDecorator(this);
    }

    public T Instance
    {
        get { return _instance; }
    }

    public object AdditionalInformation { get; set; }
}
// use it like this
Decorator<MyClass> myDecor = myObj.GetDecorator<Decorator<MyClass>>();

If you cannot derive, then you must store info in some static class. But, as wcoenen commented, you would need to clear that info or you'd get memory leaks. Clearing is error prone and not always possible, so it's better to go with the first approach. For example (not thread safe, you must add locking if you plan to use it in multithreaded apps):

static public class Decorators
{
    static Dictionary<object,Dictionary<Type,object>> instance = new Dictionary<object,Dictionary<Type,object>>();
    public static void AddDecorator<T,D>(this T obj, D decor)
    {
        Dictionary<Type,object> d;
        if (!instance.TryGetValue(obj, out d))
        {
            d = new Dictionary<Type,object>();       
            instance.Add(obj, d);
        }
        d[typeof(D)]=decor;
    }

    public static D GetDecorator<T,D>(this T obj)
    {
        // here must be double TryGetValue, but I leave it to you to add it  
        return (D) instance[obj][typeof(D)];
    }

    public static T ClearDecorators(this T obj) { instance.remove(obj); }

}

// Decorator<T> code stays the same, but without type constraint
Dmitry
A static class to keep object-to-decorators mappings will cause the decorated objects to never be garbage collected. Adding a RemoveDecorator to fix this will just complicate things more: when should it be called and by whom? You'll introduce a whole class of memory leak bugs.
Wim Coenen
Thanks for spotting this, wcoenen. You are completely right, so I've updated the answer.
Dmitry
A: 

It is a different kind of question.

And the answer is, no, you cannot do it.

MSFT did it realising you would be blowing your one shot at the base class in a single inheritance limitation (they call it simplification), a layman excuse and not the root of the problem. Moreover, it would make the compiler and language far more complicated (re: capable/expressive) than it currently is.

Thus they chose the term "generics". They made it their own "concept". It was presenting "new idioms".

[ That adds up to three quoted, pretty bad, excuses above. Only three and none of them are new or true concepts and hardly fresh idioms in history of computing ]

However it had to distinguish itself as something "new", as a constraint "feature", and target "audience" they were aiming at. Three extra excuses again. Soon another wave of excuses followed as people complained about value type support, violating of DRYs with repeated usings etc etc. Another glaring example is overuse of interfaces in combination with generics, functional 3.0 workarounds etc, all for a feature that shows that generics are a lousy type-interface mechanism.

As your collegue is looking for a compile-time feature for a good reason, I can only suggest they will eventually implement something similar 25 years later to what he already has in his arsenal. It would be a compiler-as-a-service workaround (not a new idea again) for an SI system. This is when Anders will finally have the guts to catch up with meta-programming techniques (refs: http://www.getsomenuts.tv/ )

The overstated point that there is an obvious optimisation as a side-effect of runtime approach (reusing IL, metadata or generating less bloated code) is mute. Reason is very simple, it is trivial to do for VM systems.

In conclusion, those were the two driving limitations of Smalltalk and Java idea that made the MS research teams experiment, post some papers and implement the generics feature as a Rotor hack initially. Thereafter it made its way into CLR 2.0.

*The debris of that design was even left with method names containing hack in the shipped version on your hard disk.*

rama-jka toti
-1 not because I'm a ".NET sheep" as your profile suggests, but because your answer is an unreadable rant.
Wim Coenen
edited.. please let me know if I can make it even more readable.
rama-jka toti