【问题标题】:Decorator with generic base class具有通用基类的装饰器
【发布时间】:2018-04-25 03:48:25
【问题描述】:

上周我的一位同事问我是否可以在 C# 中从其泛型参数扩展泛型类。他说这在 C++ 中是可能的。 他想要的实际上是有道理的。他想要一个通用装饰器来用附加信息注释任意类。比如:

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

这样他现在可以在任何地方使用这个通用装饰器而不是 T。

我能想到的最相似的东西是一个包含原始对象、附加信息和隐式转换的容器类。

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;
    }
}

但这不一样,因为隐式转换只是一种方式。例如,他不能将其用作方法的返回类型,因为在隐式转换后附加信息会丢失。

有人有更好的主意吗?

【问题讨论】:

标签: c# generics


【解决方案1】:

如果您可以从某个基类派生所有可装饰类,那么您可以尝试将装饰器存储在该基类中并使其信息可恢复。这是保证包含一些错误的示例代码,但您可以理解。

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>>();

如果您不能派生,那么您必须将信息存储在某个静态类中。但是,正如 wcoenen 评论的那样,您需要清除该信息,否则您会遇到内存泄漏。清算容易出错,而且并不总是可行的,所以最好采用第一种方法。例如(不是线程安全的,如果您打算在多线程应用程序中使用它,则必须添加锁定):

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

【讨论】:

  • 保持对象到装饰器映射的静态类将导致装饰对象永远不会被垃圾回收。添加一个 RemoveDecorator 来解决这个问题只会让事情变得更加复杂:什么时候应该调用它以及由谁调用?您将介绍一整类内存泄漏错误。
  • 感谢您发现这一点,wcoenen。你是完全正确的,所以我已经更新了答案。
猜你喜欢
  • 2018-12-16
  • 2023-03-13
  • 2016-08-18
  • 1970-01-01
  • 2021-06-24
  • 2016-01-19
  • 1970-01-01
  • 2017-03-24
  • 2014-01-14
相关资源
最近更新 更多