3

我有许多实现这样的接口的类:

public interface IProcessor
{
    Foo Foo { get; set; }
    int Process();
}

我有一家生产这些的工厂,我提前不知道会生产多少。问题是检索必要的对象是昂贵的,并且在调用该属性之前Foo实际上并未使用该属性。IProcessorProcess

所以如果我的工厂有这样的方法:

public IProcessor CreateProcessor(int fooId)
{
    Foo foo = this.Context.GetFoo(fooId);

    return new ImplementedProcessor { Foo = foo };
}

GetFoo每次都要打电话。我真正想要的是这样的:

public IProcessor CreateProcessor(int fooId)
{
    this.CurrentlyTrackedFooIds.Add(fooId);

    return new ImplementedProcessor { Foo = what??? };
}

这个想法是,在ImplementedProcessor需要Foo执行它的Process方法之前,它将排队 fooId,以便可以像这样批量检索它们:

this.Context.GetAllFoosAtOnceCheaply(this.CurrentlyTrackedFooIds);

是否有任何延迟加载模式可以帮助解决这样的问题?我可以完全控制整个项目,因此我不受现有IProcessorIProcessorFactory界面的限制。我可以想办法做到这一点(可能是某种委托?),但它们看起来都不是很干净,我想通过在项目中拥有一百万个不同的实现来避免这种事情失控。任何帮助将不胜感激!

4

2 回答 2

4

所以有两种类型可以让我们在这里的生活变得超级轻松。第一个是Lazy。当你构造一个Lazy对象时,你给它一个返回值的函数。然后,当您询问 a 的值Lazy时,如果它没有启动,它会启动该函数,如果它不完整则等待该函数,然后返回该值。它从不调用该函数两次。如果Value从未要求它,它甚至不会触发该功能一次。

然后我们可以创建一个ConcurrentDictionary对象Lazy,这样我们就可以确保获得Lazy我们拥有的任何现有对象。请注意,它的GetOrAdd方法明显是原子的。我们可以确定字典永远不会Lazy用另一个对象覆盖现有对象;它要么返回一个现有的,要么设置一个新的。这意味着我们永远不会调用代表相同 ID 的Value两个不同Lazy对象。

毕竟,没有太多的代码:

private  ConcurrentDictionary<int, Lazy<IProcessor>> lookup =
        new ConcurrentDictionary<int, Lazy<IProcessor>>();
public IProcessor CreateProcessor(int fooId)
{
    var lazy = lookup.GetOrAdd(fooId, new Lazy<IProcessor>(Context.GetFoo));

    return new ImplementedProcessor { Foo = lazy.Value };
}
于 2013-11-01T20:21:25.290 回答
3

从 .NET 4.0 开始,该框架Lazy<T>为此提供了一个类:

public interface IProcessor {
    Foo Foo { get; }
    int Process();
}

internal class ImplementedProcessor {
    internal Lazy<Foo> LazyFoo {get;set;}
    public Foo Foo {
        get {
            return LazyFoo.Value;
        }
    }
    public int Process() {
        ...
    }
}

public IProcessor CreateProcessor(int fooId) {
    return new ImplementedProcessor {
        LazyFoo = () => this.Context.GetFoo(fooId)
    };
}

但是,这不会Foo批量检索 s - 该过程将一个接一个地进行。Process()只有当您的某些项目从未被调用时,您才会节省一些时间ImplementedProcessor

为此,您需要创建一个缓存,该缓存在检索 first 时延迟初始化Foo,并留在那里等待剩余Foo的 s 获取缓存值。Lazy<T>不过,这不太适合。考虑在调用之前添加一个方法来获取缓存IProcessor所需的 s ,并添加一个检索所有感兴趣的 ID 的类。fooIdProcess

于 2013-11-01T20:20:12.690 回答