我目前正忙于为现有技术实现流畅的界面,这将允许类似以下代码段的代码:
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;
}
也就是说,初始示例中包含的组成部分语句越多,就需要创建更多的对象,以便将参数传递给链中的后续对象,直到实际语句最终可以执行。
我对一些观点感兴趣:使用上述技术实现的流畅界面是否会显着影响使用它的应用程序的运行时性能?在运行时性能方面,我指的是 速度和内存使用方面。
请记住,必须为非常短暂的时间跨度创建可能存在大量临时的参数保存对象,我认为这可能会给垃圾收集器带来一定的压力。
如果您认为会对性能产生重大影响,您是否了解实现流畅接口的更好方法?
答案 0 :(得分:5)
一般来说,具有非常小的生命周期的对象正是GC最有效处理的对象类型,因为大多数对象在下一个次要集合运行时将会死亡 - 并且在任何体面的GC实现中,次要集合的成本与 live 对象的总大小成正比。因此,短期对象的成本非常低,而且它们的分配只意味着向上碰撞指针,这很快。
所以我想说:可能没有显着的性能影响。
答案 1 :(得分:2)
托马斯非常正确,因为世代GC针对这种短期对象的分配和收集进行了优化。但是,像OCaml这样的函数式语言的GC比.NET更加优化,但是,在将多个参数应用于curried函数的等效情况下,它们仍然竭尽全力避免这种情况。具体来说,他们使用一种称为大步语义的技术,其中编译器在编译时删除所有中间体,因此GC从不会看到任何这些。
在.NET上,值类型可能会让你自己解决这个问题。