-1

I am new to C# and programming in general and was trying to find out how to implement a builder that can support nested objects when I found the following question: Builder pattern with nested objects

The accepted solutions work but I realize I don't fully understand what is going on in the code.

Especially this part that is defining the Action:

public ReceiptBuilder WithItem(string text, Action<ReceiptItemBuilder> itemBuilder)
{
    var rib = new ReceiptItemBuilder(text);
    itemBuilder(rib);
    r.AddItem(rib.Build());
    return this;
}

Would be nice if someone could explain what happens during this call?

itemBuilder(rib);
7oy8oy
  • 23
  • 5
  • `itemBuilder` is a `Action` delegate that takes a `ReceiptItemBuilder` which means it can be called like a method with the signature `void itemBuilder(ReceiptItemBuilder x)`. Delegates are basically how you can pass functions in C#. – juharr Oct 02 '19 at 12:31
  • 1
    Have a look at these tutorials: [Delegates Level1](https://www.tutorialspoint.com/csharp/csharp_delegates.htm) and [Delegates Level2](https://www.tutorialsteacher.com/csharp/csharp-delegates) and [Anonymous Methods Level1](https://www.tutorialspoint.com/csharp/csharp_anonymous_methods.htm) and [Anonymous Methods Level2](https://www.tutorialsteacher.com/csharp/csharp-anonymous-method) –  Oct 02 '19 at 12:38
  • Also: [Action](https://www.tutorialsteacher.com/csharp/csharp-action-delegate) and [Func](https://www.tutorialsteacher.com/csharp/csharp-func-delegate) –  Oct 02 '19 at 12:45
  • @OlivierRogier don't forget [Predicate](https://www.tutorialsteacher.com/csharp/csharp-predicate) – MindSwipe Oct 02 '19 at 12:45
  • Thanks for the replies. I will for sure look into those tutorials. @juharr But what happens when you call itemBuilder and pass it the ReceiptItemBuilder – 7oy8oy Oct 02 '19 at 12:54
  • It will pass your ReceiptItemBuilder to whatever function you passed in. In the linked question an example would be `i => i.WithIngredients("Ingredients1")` which means it will end up calling `WithIngredients("Ingredients1")` on the `rib` object that is pass to the delegate. It basically lets you inject code into a method. – juharr Oct 02 '19 at 13:03

1 Answers1

0

Action is a delegate type, and in C# delegate types (like Action, Func and Predicate, or just straight up delegate) are ways of passing methods around as parameters. Delegate types can be called directly by doing itemBuilder(rib), this executes the Action (Method) itemBuilder with the parameter rib.

What it does is this:

First we declare the method with the Action parameter

public ReceiptBuilder WithItem(string text, Action<ReceiptItemBuilder> itemBuilder)

Action<ReceiptItemBuilder> Limits our parameter to methods that return a void and take one parameter of type ReceiptItemBuilder.

So we can now declare a method that fits these criteria. Here's mine:

public void ItemWithIngredient(ReceiptItemBuilder itemBuilder)
{
    itemBuilder.WithIngredients("Ingredients1");
}

And pass to WithItem like so:

WithItem("Item1", ItemWithIngredient);

Now this isn't of much use as we can't change the Ingredients, as we hardcoded it. So instead we us something called a anonymous method, and we define it with a lambda expression like so:

WithItem("Item1", i => i.WithIngredients("Ingredients1"));

The i => ... part basically declares a method and uses it. i here is the parameter and is of type ReceiptItemBuilder.

This Answer is kinda rambly and I'm bad at explaining things. I highly suggest you check out the Links posted in the comments by Olivier Rogier but here's a quick overview over the three standard delegate types:

Action: Action is a method that returns a void and takes no parameters. It has some Overloads like Action<T> which represents a method with one parameter of type T (replace T with any type you want). There are more overloads for more parameters (Action<T1, T2>, Action<T1, T2, T3> ...)

Func: Func<T> is a method that returns T and takes no parameters (there is no Func as Func always returns something). There are overloads for this as well, these take parameters (Func<TInput, TOutput>, Func<TInput1, TInput2, TOutput> ...)

Predicate: A Predicate<T> always takes one parameter and returns a bool

MindSwipe
  • 7,193
  • 24
  • 47
  • Thanks for taking the time to try to explain this concept. You claim to be bad at explaining things but your explanation together with the other previous comments has helped make this a lot clearer for me. I realize I have a long way to go, but at least now I am not completely lost. – 7oy8oy Oct 02 '19 at 14:08