3

我正在尝试创建一个抽象的通用管理器,它将提供添加/删除的事件,并提供用于创建和完成项目的抽象方法。

下面是我提出的代码,但是在 TestClass 构造函数中,将 TestManager 的 ManagerBase 参数传递给 base 会产生无效转换的编译器错误,即使 TestManager 肯定是 ManagerBase。

可以使 Manager 字段成为“对象”并在运行时进行投射,但这会非常难看。

是否有可能使它工作,或者我的方向错误?

using System;
using System.Collections.Generic;
using System.Diagnostics;

namespace ManagerTest
{
    class Program
    {
        static void Main(string[] args)
        {
            var Manager = new TestManager();
            Manager.ItemAdded += (manager, item) => Debug.WriteLine("Item added: " + item.Id);
            Manager.ItemRemoved += (manager, item) => Debug.WriteLine("Item removed: " + item.Id);

            var entity1 = Manager.Create("entity1");
            var entity2 = Manager.Create("entity2");
            Manager.Remove("entity1");
            var entity3 = Manager.Create("entity3");
            Manager.Remove("entity3");
            Manager.Remove("entity4");
        }
    }

    class TestClass : EntityBase, IDisposable
    {
        public TestClass(string id, TestManager manager) : base(id, manager) { Debug.WriteLine(Id + " - ctor"); }
        public void Dispose() { Debug.WriteLine(Id + " - disposed"); }
    }

    class TestManager : ManagerBase<TestClass>
    {
        protected override TestClass CreateInternal(string key) { return new TestClass(key, this); }
        protected override void FinalizeRemove(TestClass item) { item.Dispose(); }
    }

    abstract class EntityBase
    {
        public string Id { get; private set; }
        public ManagerBase<EntityBase> Manager { get; private set; }

        public EntityBase(string id, ManagerBase<EntityBase> manager)
        {
            this.Id = id;
            this.Manager = manager;
        }
    }

    abstract class ManagerBase<T> where T : EntityBase
    {
        public event Action<ManagerBase<T>, T> ItemAdded;
        public event Action<ManagerBase<T>, T> ItemRemoved;

        private readonly Dictionary<string, T> Storage = new Dictionary<string, T>();

        protected abstract T CreateInternal(string key);
        protected abstract void FinalizeRemove(T item);

        public T Create(string key)
        {
            T newItem = null;

            lock (Storage)
            {
                if (!Storage.ContainsKey(key))
                {
                    newItem = CreateInternal(key);

                    Storage.Add(key, newItem);

                    ItemAdded.SafeInvoke(this, newItem);
                }
            }

            return newItem;
        }

        public T Get(string key)
        {
            lock (Storage)
                return Storage.ContainsKey(key) ? Storage[key] : null;
        }

        public bool Contains(string key)
        {
            lock (Storage)
                return Storage.ContainsKey(key);
        }

        public bool Remove(string key)
        {
            bool returnValue = false;

            lock (Storage)
                if (Storage.ContainsKey(key))
                {
                    var item = Storage[key];
                    returnValue = Storage.Remove(key);
                    ItemRemoved.SafeInvoke(this, item);
                    FinalizeRemove(item);
                }

            return returnValue;
        }
    }

    static class Extensions
    {
        public static void SafeInvoke<T1, T2>(this Action<T1, T2> action, T1 arg1, T2 arg2)
        {
            var actionCopy = action;
            if (actionCopy != null)
                actionCopy(arg1, arg2);
        }
    }
}
4

2 回答 2

3

更正后的代码

using System;
using System.Collections.Generic;
using System.Diagnostics;

namespace ManagerTest
{
    class Program
    {
        static void Main(string[] args)
        {
            var Manager = new TestManager();
            Manager.ItemAdded += (manager, item) => Debug.WriteLine("Item added: " + item.Id);
            Manager.ItemRemoved += (manager, item) => Debug.WriteLine("Item removed: " + item.Id);

            var entity1 = Manager.Create("entity1");
            var entity2 = Manager.Create("entity2");
            Manager.Remove("entity1");
            var entity3 = Manager.Create("entity3");
            Manager.Remove("entity3");
            Manager.Remove("entity4");
        }
    }

    class TestClass : EntityBase, IDisposable
    {
        public TestClass(string id, TestManager manager) : base(id, manager) { Debug.WriteLine(Id + " - ctor"); }
        public void Dispose() { Debug.WriteLine(Id + " - disposed"); }
    }

    class TestManager : ManagerBase<TestClass>, IManagerBase<TestClass>
    {
        protected override TestClass CreateInternal(string key) { return new TestClass(key, this); }
        protected override void FinalizeRemove(TestClass item) { item.Dispose(); }
    }

    abstract class EntityBase
    {
        public string Id { get; private set; }
        public IManagerBase<EntityBase> Manager { get; private set; }

        public EntityBase(string id, IManagerBase<EntityBase> manager)
        {
            this.Id = id;
            this.Manager = manager;
        }
    }

    interface IManagerBase<out T>
        where T : EntityBase
    {
        event Action<IManagerBase<T>, T> ItemAdded;
        event Action<IManagerBase<T>, T> ItemRemoved;
        T Create(string key);
        T Get(string key);
        bool Contains(string key);
        bool Remove(string key);
    }

    abstract class ManagerBase<T> : IManagerBase<T> where T : EntityBase
    {
        public event Action<IManagerBase<T>, T> ItemAdded;
        public event Action<IManagerBase<T>, T> ItemRemoved;

        private readonly Dictionary<string, T> Storage = new Dictionary<string, T>();

        protected abstract T CreateInternal(string key);
        protected abstract void FinalizeRemove(T item);

        public T Create(string key)
        {
            T newItem = null;

            lock (Storage)
            {
                if (!Storage.ContainsKey(key))
                {
                    Storage.Add(key, CreateInternal(key));

                    ItemAdded.SafeInvoke(this, newItem);
                }
            }

            return newItem;
        }

        public T Get(string key)
        {
            lock (Storage)
                return Storage.ContainsKey(key) ? Storage[key] : null;
        }

        public bool Contains(string key)
        {
            lock (Storage)
                return Storage.ContainsKey(key);
        }

        public bool Remove(string key)
        {
            bool returnValue = false;

            lock (Storage)
                if (Storage.ContainsKey(key))
                {
                    var item = Storage[key];
                    returnValue = Storage.Remove(key);
                    ItemRemoved.SafeInvoke(this, item);
                    FinalizeRemove(item);
                }

            return returnValue;
        }
    }

    static class Extensions
    {
        public static void SafeInvoke<T1, T2>(this Action<T1, T2> action, T1 arg1, T2 arg2)
        {
            var actionCopy = action;
            if (actionCopy != null)
                actionCopy(arg1, arg2);
        }
    }
}
于 2013-09-19T21:54:18.537 回答
2

问题在于 ManagerBase 中 T 参数的不变性。你不能分配ManagerBase<Derived>ManagerBase<Base>. 如果参数是协变的,您将能够做到这一点,但这只能在接口中完成,而不是在类中。所以也许尝试让managerbase成为一个接口?喜欢IManager<out T>

这篇文章很好地解释了协方差/逆变的东西:http: //tomasp.net/blog/variance-explained.aspx/

于 2013-09-19T21:38:27.143 回答