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