代码之家  ›  专栏  ›  技术社区  ›  Lobstrosity

C中的通用标识映射。不需要公共构造函数

  •  5
  • Lobstrosity  · 技术社区  · 15 年前

    我正在尝试实现 identity map 使用仿制药。我在实体的映射上有一个抽象类、实体和派生约束。因为我的映射需要能够实例化实体,所以我的映射也有一个构造函数约束。

    但是,为了使映射有用,实体子类不应该能够从客户机代码实例化,这意味着我需要一个内部构造函数,而不需要公共构造函数。但这与构造函数约束冲突。

    我有什么东西不见了吗?是否有某种方法可以重构它以获得所需的结果?

    以下代码按原样编译,但理想情况下,实体的子类构造函数将是内部的:

    public abstract class Entity
    {
        public int Id { get; protected internal set; }
    }
    
    public sealed class Widget : Entity
    {
        // Client code should not be allowed to instantiate entities.
        // But the constraints on EntityMap require that entities have
        // a public constructor.
        public Widget() { }
    }
    
    public sealed class Gadget : Entity
    {
        public Gadget() { }
    }
    
    // The new() constraint is required so that Get() can instantiate Ts.
    public class EntityMap<T> where T : Entity, new()
    {
        private Dictionary<int, T> _entities = new Dictionary<int, T>();
        private object _getLock = new object();
    
        public T Get(int id)
        {
            lock (_getLock)
            {
                if (!_entities.ContainsKey(id))
                    _entities.Add(id, new T() { Id = id });
            }
    
            return _entities[id];
        }
    
        // Client code should not be allowed to instantiate maps.
        internal EntityMap() { }
    }
    
    // Ideally, the client would only be able to obtain Entity
    // references through EntityMaps, which are only accessible
    // through the ApplicationMap.
    public static class ApplicationMap
    {
        public static EntityMap<Widget> Widgets = new EntityMap<Widget>();
        public static EntityMap<Gadget> Gadgets = new EntityMap<Gadget>();
    }
    
    2 回复  |  直到 15 年前
        1
  •  9
  •   Jon Skeet    15 年前

    不需要构造函数约束,而是传递 Func<T> 到映射构造函数。这样,构造函数可以是内部的,但映射仍然可以有效地调用它:

    public class EntityMap<T> where T : Entity
    {
        private readonly Dictionary<int, T> _entities = new Dictionary<int, T>();
        private readonly object _getLock = new object();
        private readonly Func<T> _entityGenerator;
    
        public T Get(int id)
        {
            lock (_getLock)
            {
                T ret;
                if (!_entities.TryGetValue(id, ret))
                {
                    ret = entityGenerator();
                    newEntity[id] = ret;
                    ret.Id = id;
                }
    
                return ret;
            }
        }
    
        internal EntityMap(Func<T> entityGenerator)
        {
            _entityGenerator = entityGenerator;
        }
    }
    

    然后初始化它:

    EntityMap<Widget> widgetMap = new EntityMap(() => new Widget());
    

    你可能会让它成为 Func<int, T> 相反,让委托负责创建具有正确ID的实体。这样,您就可以使您的ID正确地只读,并将其作为 Entity 构造函数。

    (我冒昧地 Get 方法更有效,顺便说一句)

        2
  •  2
  •   Lobstrosity    15 年前

    多亏了乔恩,以下是工作代码:

    public abstract class Entity
    {
        private readonly int _id;
    
        public int Id
        {
            get { return _id; }
        }
    
        internal Entity(int id)
        {
            _id = id;
        }
    }
    
    public sealed class Widget : Entity
    {
        internal Widget(int id) : base(id) { }
    }
    
    public sealed class Gadget : Entity
    {
        internal Gadget(int id) : base(id) { }
    }
    
    public class EntityMap<T> where T : Entity
    {
        private readonly Dictionary<int, T> _entities = new Dictionary<int, T>();
        private readonly object _getLock = new object();
        private readonly Func<int, T> _entityGenerator;
    
        public T Get(int id)
        {
            lock (_getLock)
            {
                T entity;
    
                if (!_entities.TryGetValue(id, out entity))
                    _entities[id] = entity = _entityGenerator(id);
    
                return entity;
            }
        }
    
        internal EntityMap(Func<int, T> entityGenerator)
        {
            _entityGenerator = entityGenerator;
        }
    }
    
    public static class ApplicationMap
    {
        public static readonly EntityMap<Widget> Widgets = new EntityMap<Widget>(id => new Widget(id));
        public static readonly EntityMap<Gadget> Gadgets = new EntityMap<Gadget>(id => new Gadget(id));
    }