7

The Dart programming language has support for method cascades. Method cascades would allow the following Silverlight/WPF C# code:

var listBox = new ListBox();

listBox.Width = 200;
listBox.MouseEnter += (s, e) => Console.WriteLine("MouseEnter");

var button1 = new Button() { Content = "abc" };
button1.Click += (s, e) => Console.WriteLine("button1.Click");

listBox.Items.Add(button1);

var button2 = new Button() { Content = "def" };
button2.Click += (s, e) => Console.WriteLine("button2.Click");

listBox.Items.Add(button2);

ContentPanel.Children.Add(listBox);

to be written instead as:

ContentPanel.Children.Add(
    new ListBox()
        ..Width = 200
        ..MouseEnter += ((s, e) => Console.WriteLine("MouseEnter"))
        ..Items.Add(
            new Button()
                ..Content = "abc";
                ..Click += ((s, e) => Console.WriteLine("button 1 Click")))
        ..Items.Add(
            new Button()
                ..Content = "def";
                ..Click += (s, e) => (Console.WriteLine("button 2 Click"))));

My question is, is there a way to simulate or closely approximate method cascades in C#?

Here's one approach I came up with. Given this extension method:

public static T Call<T>(this T obj, Action<T> proc)
{
    proc(obj);

    return obj;
}

the above example can be written as follows:

ContentPanel.Children.Add(
    new ListBox().Call(o => {
            o.Width = 200;
            o.MouseEnter += (s, e) => Console.WriteLine("MouseEnter");
            o.Items.Add(
                new Button().Call(b => {
                        b.Content = "abc";
                        b.Click += (s, e) => Console.WriteLine("button 1 Click"); }));
            o.Items.Add(
                new Button().Call(b => {
                    b.Content = "def";
                    b.Click += (s, e) => Console.WriteLine("button 2 Click"); })); }));

I wouldn't argue that that's pretty. :-) But it does essentially enable a fluent style to be applied.

Darshan Rivka Whittle
  • 32,989
  • 7
  • 91
  • 109
dharmatech
  • 8,979
  • 8
  • 42
  • 88
  • 1
    Are you simply not looking for [Object initializers](http://msdn.microsoft.com/en-us/library/vstudio/bb384062.aspx)? – Simon Whitehead Jan 09 '13 at 04:19
  • 2
    @SimonWhitehead Object initializers only work with properties. The example includes events and method calls. – juharr Jan 09 '13 at 04:28
  • 4
    I think what you have is about as close as you can get. And I honestly don't think it's much uglier than your Dart example. – juharr Jan 09 '13 at 04:34
  • @juharr Indeed.. silly me! :) – Simon Whitehead Jan 09 '13 at 04:39
  • 2
    You can always switch to VB.NET and use `With` blocks... – phoog Jan 09 '13 at 05:13
  • Seems like this might be useful if the class didn't already impliment IDisposable. If IDisposable, though, then why not just use a Using block? – William Jan 11 '13 at 21:04
  • If you'd like to see method cascades in C#, consider voting for it on [UserVoice](http://visualstudio.uservoice.com/forums/121579-visual-studio/suggestions/5182443-add-method-cascades-to-c-). – dharmatech Dec 12 '13 at 19:10

2 Answers2

4

I think you can reach close to what you want to achieve by using fluent interface. It will allow you to chain methods to create and initialize objects in one statement.

You can get something like that:

Fluent fluent = new Fluent();
var panel = fluent.CreateControlPanel().Children()
                .AddListBox().SetWidth(200).AddMouseEnterEvent((s, e) => { }).Create()
                .AddTextBox().SetText("Foo").Create()
                .GetControlPanel();

The idea is that a method returns an object allowing to initialize another object. A chain of initializer can call at any item a "finalizer" method (above Create) that returns the original object (above Children) to continue to add other objects or configure the initial one.

So for example in AddListBox returns an object of type ListBoxSetup which has a bunch of methods like SetWidth or AddMouseEnterEvent. In this case Children will also be a special object (like of type ChildSetup) which has a bunch of methods such as AddListBox or AddTextBox. Each of the method has in charge to create an object of type ListBox or TextBox or setup properties of the underlying object to be created. Fluent will have a method that returns your whole object structure correctly setup.

Take a look to this link: http://blog.raffaeu.com/archive/2010/06/26/how-to-write-fluent-interface-with-c-and-lambda.aspx

Here's an example of the underlying code create to end up with the above. Of course the code could be greatly improved in its architecture but it's here just for the sake of the example.

public class Fluent
{
    public ControlPanelCreator CreateControlPanel()
    {
        return new ControlPanelCreator(new StackPanel(), this);
    }
}

public class ControlPanelCreator
{
    #region Fields
    private Fluent fluent;
    private Panel panel;
    #endregion

    #region Constructors
    internal ControlPanelCreator(Panel panel, Fluent fluent)
    {
        this.fluent = fluent;
        this.panel = panel;
    }
    #endregion

    #region Methods
    public ControlPanelChildrenCreator Children()
    {
        return new ControlPanelChildrenCreator(this.panel, this);
    }
    #endregion
}

public class ControlPanelChildrenCreator
{
    #region Fields
    private ControlPanelCreator panelCreator;
    private Panel panel;
    #endregion

    #region Constructors
    internal ControlPanelChildrenCreator(Panel panel, ControlPanelCreator panelCreator)
    {
        this.panel = panel;
        this.panelCreator = panelCreator;
    }
    #endregion

    #region Methods
    public ListBoxCreator AddListBox()
    {
        ListBox listBox = new ListBox();
        this.panel.Children.Add(listBox);
        return new ListBoxCreator(listBox, this);
    }

    public TextBoxCreator AddTextBox()
    {
        TextBox textBox = new TextBox();
        this.panel.Children.Add(textBox);
        return new TextBoxCreator(textBox, this);
    }

    public Panel GetControlPanel()
    {
        return this.panel;
    }
    #endregion
}

public class ListBoxCreator
{
    #region Fields
    private ListBox listbox;
    private ControlPanelChildrenCreator parentCreator;
    #endregion

    #region Constructors
    internal ListBoxCreator(ListBox listBox, ControlPanelChildrenCreator parentCreator)
    {
        this.listbox = listBox;
        this.parentCreator = parentCreator;
    }
    #endregion

    #region Methods
    public ListBoxCreator SetWidth(int width)
    {
        this.listbox.Width = width;
        return this;
    }

    public ListBoxCreator AddMouseEnterEvent(Action<object, MouseEventArgs> action)
    {
        this.listbox.MouseEnter += new MouseEventHandler(action);
        return this;
    }

    public ControlPanelChildrenCreator Create()
    {
        return this.parentCreator;
    }
    #endregion
}

public class TextBoxCreator
{
    #region Fields
    private TextBox textBox;
    private ControlPanelChildrenCreator parentCreator;
    #endregion

    #region Constructors
    internal TextBoxCreator(TextBox textBox, ControlPanelChildrenCreator parentCreator)
    {
        this.textBox = textBox;
        this.parentCreator = parentCreator;
    }
    #endregion

    #region Methods
    public TextBoxCreator SetText(string defaultText)
    {
        this.textBox.Text = defaultText;
        return this;
    }

    public ControlPanelChildrenCreator Create()
    {
        return this.parentCreator;
    }
    #endregion
}
Guillaume
  • 1,782
  • 1
  • 25
  • 42
0

I supported previous answer. Few things I like to add, as I have created similar type of things.

There are two things, either you are having single class doing things. Means Form, has addcolor, addData etc and may be form has button and than button has color

Now in this case you need to chain using interface, means method return type will be interface, and all interface is implemented by that class and that methods return "this" only.

This will do the trick, while you create object of interface. And then chain it across. It will be difficult to give example here, but if you still want I can provide example.

Let me know if any further details required

kunjee
  • 2,739
  • 1
  • 23
  • 38