9

我目前正忙于为现有技术实现一个流畅的接口,这将允许类似于以下代码段的代码:

using (var directory = Open.Directory(@"path\to\some\directory"))
{
    using (var file = Open.File("foobar.html").In(directory))
    {
        // ...
    }
}

为了实现这样的结构,需要类来积累参数并将它们传递给其他对象。例如,要实现该Open.File(...).In(...)构造,您需要两个类:

// handles 'Open.XXX':
public static class OpenPhrase
{
    // handles 'Open.File(XXX)':
    public static OpenFilePhrase File(string filename)
    {
        return new OpenFilePhrase(filename);
    }

    // handles 'Open.Directory(XXX)':
    public static DirectoryObject Directory(string path)
    {
        // ...
    }
}

// handles 'Open.File(XXX).XXX':
public class OpenFilePhrase
{
    internal OpenFilePhrase(string filename)
    {
        _filename = filename
    }

    // handles 'Open.File(XXX).In(XXX):
    public FileObject In(DirectoryObject directory)
    {
        // ...
    }

    private readonly string _filename;
}

也就是说,初始示例等语句的组成部分越多,需要创建的对象就越多,以便将参数传递给链中的后续对象,直到实际语句最终可以执行。

问题:

我对一些观点感兴趣:使用上述技术实现的流畅接口是否会显着影响使用它的应用程序的运行时性能?对于运行时性能,我指的速度和内存使用方面。

请记住,可能只需要在非常短的时间跨度内创建大量临时的、保存参数的对象,我认为这可能会给垃圾收集器带来一定的压力。

如果您认为有显着的性能影响,您知道实现流畅接口的更好方法吗?

4

2 回答 2

5

一般来说,生命周期非常短的对象正是 GC 处理效率最高的对象,因为它们中的大多数将在下一次次要收集运行时死亡——在任何体面的 GC 实现中,成本次要集合与活动对象的总大小成正比。因此,短期对象的成本非常低,并且它们的分配只意味着向上碰撞一个指针,这很快。

所以我会说:可能没有显着的性能影响。

于 2010-03-31T21:01:38.707 回答
2

Thomas 非常正确,分代 GC 正是针对这种短期对象的分配和收集进行了优化。然而,像 OCaml 这样的函数式语言的 GC 比 .NET 更加优化,但是,在将多个参数应用于柯里化函数的等效情况下,它们仍然竭尽全力避免这种情况。具体来说,他们使用一种称为大步语义的技术,其中编译器在编译时删除所有中间体,因此 GC 永远不会看到任何这些。

在 .NET 上,值类型很可能让您自己解决这个问题。

于 2012-05-14T23:08:39.627 回答