【问题标题】:Generic Identity Map in C#. Don't want public constructorC# 中的通用身份映射。不想要公共构造函数
【发布时间】:2009-09-11 01:02:48
【问题描述】:

我正在尝试使用泛型实现identity map。我的实体地图上有一个抽象类、实体和派生约束。由于我的地图需要能够实例化实体,所以我的地图还具有构造函数约束。

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

我有什么遗漏吗?有什么方法可以重构它以获得想要的结果吗?

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

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

【问题讨论】:

    标签: c# design-patterns generics


    【解决方案1】:

    不需要构造函数约束,而是将Func&lt;T&gt; 传递给映射构造函数。这样构造函数可以是内部的,但地图仍然可以有效地调用它:

    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&lt;int, T&gt;,并让委托负责创建具有正确 ID 的实体。这样您就可以正确地将您的 ID 设为只读,将其作为 Entity 构造函数的参数。

    (顺便说一句,我冒昧地让您的 Get 方法更高效。)

    【讨论】:

    • 使用 TryGetValue 代替 ContainsKey 有什么好处?是速度问题吗?
    【解决方案2】:

    感谢 Jon,这是工作代码:

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

    【讨论】:

    • 您最好将此作为对原始帖子的编辑而不是作为替代答案发布。
    猜你喜欢
    • 1970-01-01
    • 2017-04-30
    • 1970-01-01
    • 2011-03-01
    • 1970-01-01
    • 1970-01-01
    • 2012-04-12
    • 2011-02-08
    • 2010-10-13
    相关资源
    最近更新 更多