12

我有这个简单的课程:

public class DataBag
{
    public string UserControl { get; set; }
    public string LoadMethod { get; set; }
    public dynamic Params { get; set; }
    public int Height { get; set; }

    public DataBag(string Control, 
        object vars, string lm)
    {
        UserControl = Control;
        LoadMethod = lm;
        Params = vars;
        Height = 0;
    }
}

然后我想为它创建一个装饰器,它会添加一堆它自己的属性。问题是提供对装饰属性的访问的最简洁和优雅的方式是什么?

到目前为止,我有两个选择:或者我为get-set装饰器中的四个装饰属性中的每一个提供一对(这看起来很乏味而且很拗口,基本上这是我想要避免的)或者我继承DataBag然后DynamicObject以某种方式设法使用TryGetMember方法获取装饰属性(这是动态的,似乎不是在 C# 中做事的正确方法)。

有什么建议吗?

4

3 回答 3

9

在实现装饰器时,我通常会执行以下操作。首先 - 提取装饰对象的接口并使装饰对象实现该接口:

public interface IDataBag
{
    string UserControl { get; set; }
    string LoadMethod { get; set; }
    dynamic Params { get; set; }
    int Height { get; set; }
}

接下来 - 创建一个装饰器,它将所有调用委托给装饰对象(所有装饰器都将从这个装饰器继承):

public class DataBagDecorator : IDataBag
{
    private IDataBag _dataBag;

    public DataBagDecorator(IDataBag dataBag)
    {
        _dataBag = dataBag;
    }

    public virtual string UserControl
    {
        get { return _dataBag.UserControl; }
        set { _dataBag.UserControl = value; }
    }

    // other members
}

最后 - 创建装饰器:

public class FooDataBag : DataBagDecorator
{
    public FooDataBag(IDataBag dataBag) 
        : base(dataBag) { }

    public override string UserControl
    {
        // added behavior
        get { return "Foo" + base.UserControl; }
        set { base.UserControl = value; }
    }

    // you don't need to override other members
}

用法:

IDataBag dataBag = new FooDataBag(new DataBag());
于 2012-12-20T22:02:28.970 回答
1

更新:@JeremyDWill 正确地指出,您不能从其参数之一派生泛型类型......

如果您将装饰器视为“添加新属性的子类”,您可以执行以下操作:

public class MyDecorator<T> : T
{
    public int MyDecoratorProperty1 { get; set; }
    public int MyDecoratorProperty2 { get; set; }
}

然后,您可以创建MyDecorator<DataBag>、 和MyDecorator<OtherClass>等的实例。现有属性是可访问的,因为MyDecorator<>是特定于泛型参数的类型,并且派生自该类。

您可以创建一个包含装饰对象的包装器:

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

优点是获得装饰属性很容易:myObj.MyDecoratorProperty1. 缺点是您现在必须通过DecoratedObject成员才能到达基础对象:

DataBag bag = new DataBag("", null, null);
MyDecorator<DataBag> deco = new MyDecorator<DataBag>(bag);
deco.DecoratedObject.Height = 2;

如果您不能从装饰子类化(例如,您需要一次支持多个装饰器),则必须执行“附加属性”之类的操作……您的装饰器类必须保留原始字典对象和装饰的属性。使用一些扩展方法,您可以使这些属性“看起来”像被修饰类的本地成员,只要您知道预先修饰的类型(或愿意修饰任何对象):

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);
    }
    // ...
}

您的代码可能如下所示:

DataBag myData = new DataBag();
myData.SetMyDecoratorProperty1(7);
Console.WriteLine("prop1: {0}", myData.GetMyDecoratorProperty1());
于 2012-12-20T21:46:26.043 回答
1

这是最简单的装饰方法:

public class PrettyBag : DataBag
{
    public int Decoration1 { get; set; }
    public int Decoration2 { get; set; }
}

如果您想创建一个外观并隐藏一些 DataBag 属性而不是仅仅添加属性,那么您可以使 DataBag 的成员受到保护。

使用接口,您可以执行以下操作:

    public interface IDataBag
    {
       ...
    }
    public class DataBag : IDataBag
    {
       ...
    }
    public interface IPrettyBag : IDataBag
    {
        int Decoration1 { get; set; }
        int Decoration2 { get; set; }
    }
    public class BigBag : IPrettyBag
    {
        public int Decoration1 { get; set; }
        public int Decoration2 { get; set; }
    }
    public interface SmallBag : IPrettyBag
    {
        public int Decoration1 { get; set; }
        public int Decoration2 { get; set; }
    }
于 2012-12-20T22:00:58.213 回答