Search code examples
c#.netfluent-interface

create fluent interface for adding elements to a list


this is what I'm trying to achieve:

config.Name("Foo")
      .Elements(() => {
                         Element.Name("element1").Height(23);
                         Element.Name("element2").Height(31);
                      })
     .Foo(23);

or like this:

  .Elements(e => {
                     e.Name("element1").Height(23);
                     e.Name("element2").Height(31);
                  })
  .Foo(3232);

this is what I have for the moment:

public class Config
{
   private string name;
   private int foo;
   private IList<Element> elements = new List<Element>();

   public Config Name(string name)
   {
      this.name = name;
      return this;
   }

   public Config Foo(int x)
   {
       this.foo = x;
   }

   ... //add method for adding elements 

   class Element
   {
      public string Name { get; set; }
      public int Height { get; set; }
   }
}

anybody knows how to do this ?


Solution

  • Any reason you don't want to use object and collection initializers?

    public class Config
    {
       public string Name { get; set; }
       public int Foo { get; set; }
       public IList<Element> Elements { get; private set; }
    
       public Config()
       {
           Elements = new List<Element>();
       }
    }
    
    // I'm assuming an element *always* needs a name and a height
    class Element
    {
       public string Name { get; private set; }
       public int Height { get; private set; }
    
       public Element(string name, int height)
       {
           this.Name = name;
           this.Height = height;
       }
    }
    

    Then:

    var config = new Config
    {
        Name = "Foo",
        Elements = { 
            new Element("element1", 23),
            new Element("element2", 31)
        },
        Foo = 23
    };
    

    If you don't want to expose the list of elements directly, you could always turn that into a builder, and copy it into a more private data structure on Build:

    var config = new Config.Builder
    {
        Name = "Foo",
        Elements = { 
            new Element("element1", 23),
            new Element("element2", 31)
        },
        Foo = 23
    }.Build();
    

    This has the additional advantage that you can make Config itself immutable.

    If you always need Name to be present, just take that as a constructor parameter instead.

    While there are times where it's good to have a fluent interface with mutating (or copy-and-change) method calls, in this case I think collection/object initializers are more idiomatic C#.

    Note that if you're using C# 4 and you want to make your Element constructor calls, you can always use named arguments:

    new Element(name: "element2", height: 31)