在通用字典上键入约束

时间:2017-10-04 18:24:37

标签: c# generics

我使用反射来查找给定类型的构造函数。然后我想缓存键入其类型的构造函数,以便我可以在下次需要构造函数时动态使用它。下面的代码是这样做的,但是它要求我将构造函数存储为返回一个对象,然后将其转换为所需的类型。我希望有一种方法可以使它更安全。

private static readonly ConcurrentDictionary<Type, Func<Guid, object>> AggregateConstructors = new ConcurrentDictionary<Type, Func<Guid, object>>();

public TAggregate GetAggregate<TAggregate>(Guid aggregateId) where TAggregate : AggregateRoot
{
    var constructor = AggregateConstructors.GetOrAdd(typeof(TAggregate), GetConstructorFunc<TAggregate>());
    // Requires a cast.  
    var aggregate = (TAggregate)constructor(aggregateId);

    var history = eventStore.GetDomainEvents(aggregateId);
    aggregate.LoadFromHistory(history);

    return aggregate;
}

private Func<Guid, TAggregate> GetConstructorFunc<TAggregate>()
{
    var parameter = Expression.Parameter(typeof(Guid), "aggregateId");
    var constructor = typeof(TAggregate).GetConstructor(new[] { typeof(Guid) });
    var lambda = Expression.Lambda<Func<Guid, TAggregate>>(Expression.New(constructor, parameter), parameter);
    return lambda.Compile();
}

我想在这些方面有所建议:

private static readonly ConcurrentDictionary<Type, Func<Guid, SameTypeAsKey>> AggregateConstructors = new ConcurrentDictionary<Type, Func<Guid, SameTypeAsKey>>();

public TAggregate GetAggregate<TAggregate>(Guid aggregateId) where TAggregate : AggregateRoot
{
    var constructor = AggregateConstructors.GetOrAdd(typeof(TAggregate), GetConstructorFunc<TAggregate>());
    var aggregate = constructor(aggregateId);

    var history = eventStore.GetDomainEvents(aggregateId);
    aggregate.LoadFromHistory(history);

    return aggregate;
}

private Func<Guid, TAggregate> GetConstructorFunc<TAggregate>()
{
    var parameter = Expression.Parameter(typeof(Guid), "aggregateId");
    var constructor = typeof(TAggregate).GetConstructor(new[] { typeof(Guid) });
    var lambda = Expression.Lambda<Func<Guid, TAggregate>>(Expression.New(constructor, parameter), parameter);
    return lambda.Compile();
}

2 个答案:

答案 0 :(得分:0)

不,不可能这样做。第一个代码没问题,不需要改进。

另一个想法是使用泛型类而不是并发字典:

static class AggregateConstructors<TAggregate>
{
    public static Func<Guid, TAggregate> Value { get; private set; }
    public static TAggregate Create(Guid aggregateId) => Value(aggregateId);

    static AggregateConstructors()
    {
        var parameter = Expression.Parameter(typeof(Guid), "aggregateId");
        var constructor = typeof(TAggregate).GetConstructor(new[] { typeof(Guid) });
        var lambda = Expression.Lambda<Func<Guid, TAggregate>>(Expression.New(constructor, parameter), parameter);
        Value = lambda.Compile();
    }
}

public TAggregate GetAggregate<TAggregate>(Guid aggregateId) where TAggregate : AggregateRoot
{
    var aggregate = AggregateConstructors<TAggregate>.Create(aggregateId);

    var history = eventStore.GetDomainEvents(aggregateId);
    aggregate.LoadFromHistory(history);

    return aggregate;
}

但请注意,不建议使用泛型类中的静态变量。

答案 1 :(得分:0)

一个选项是你只需要添加另一个层来覆盖为你做演员的字典。这很容易成为一种扩展方法

public static class ExtensionMethods
{
    public static Func<Guid, TAggregate> GetOrAdd<TAggregate>(this ConcurrentDictionary<Type, Func<Guid, object>> @this, Func<Guid, TAggregate> factory)
    {
        var constructor = @this.GetOrAdd(typeof(TAggregate), (key) => factory);
        return (guid) => (TAggregate)constructor(guid);
    }
}

这可以让你做到

public TAggregate GetAggregate<TAggregate>(Guid aggregateId) where TAggregate : AggregateRoot
{
    var constructor = AggregateConstructors.GetOrAdd<TAggregate>(GetConstructorFunc<TAggregate>());
    var aggregate = constructor(aggregateId);

    var history = eventStore.GetDomainEvents(aggregateId);
    aggregate.LoadFromHistory(history);

    return aggregate;
}

另一种选择是,如果LoadFromHistory是基础AggregateRoot类的函数,则可以用object替换AggregateRoot,然后将强制转换移动到方法的末尾。

public TAggregate GetAggregate<TAggregate>(Guid aggregateId) where TAggregate : AggregateRoot
{
    var constructor = AggregateConstructors.GetOrAdd(typeof(TAggregate), GetConstructorFunc<TAggregate>());
    // Requires a cast.  
    var aggregate = constructor(aggregateId);

    var history = eventStore.GetDomainEvents(aggregateId);
    aggregate.LoadFromHistory(history);

    return (TAggregate)aggregate;
}