views:

86

answers:

1

I'm currently occupying myself with implementing a fluent interface for an existing technology, which would allow code similar to the following snippet:

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

In order to implement such constructs, classes are needed that accumulate arguments and pass them on to other objects. For example, to implement the Open.File(...).In(...) construct, you would need two classes:

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

That is, the more constituent parts statements such as the initial examples have, the more objects need to be created for passing on arguments to subsequent objects in the chain until the actual statement can finally execute.

Question:

I am interested in some opinions: Does a fluent interface which is implemented using the above technique significantly impact the runtime performance of an application that uses it? With runtime performance, I refer to both speed and memory usage aspects.

Bear in mind that a potentially large number of temporary, argument-saving objects would have to be created for only very brief timespans, which I assume may put a certain pressure on the garbage collector.

If you think there is significant performance impact, do you know of a better way to implement fluent interfaces?

+2  A: 

Generally speaking, objects with a very small lifetime are exactly the kind of objects that the GC deals most efficiently with, because most of them will be dead at the time the next minor collection runs -- and on any decent GC implementation, the cost of a minor collection is proportional to the total size of live objects. Thus, short-lived objects cost very little, and their allocation means only bumping a pointer up, which is fast.

So I would say: probably no significant performance impact.

Thomas Pornin
If the objects have finalizers, though...
TrueWill
@TrueWill: I suppose that *would* definitely impact performance. However (or at least in my code examples given above), finalizers should never be necessary, because all of these short-lifespan, argument-bearing "intermediate" objects are very lightweight and don't actually do any work other than passing on their arguments to a subsequent object. I don't think you'd ever need a finalizer for such classes.
stakx
@stakx: I agree completely.
TrueWill
@stakx: finalizers are very rarely needed at all. They serve a purpose when the instance corresponds to allocated resources which are not memory (memory is handled by the GC and the GC is good at it). Here, there is only memory.I fully agree that finalizers impact performance. A finalizable object, by nature, resists at least one minor GC collection, i.e. must still be considered live at that point.
Thomas Pornin