Reputation: 423
In the book I'm reading "Head First Design Patterns", Command Pattern there is an example where they're substituting an Interface with Lambda.
Is this something that only Java is capable of? Here is an example
// Receiver
public class Light
{
public void On() {
Console.WriteLine("Lights on");
}
public void Off() {
Console.WriteLine("Ligts off");
}
}
// Command interface
public interface ICommand
{
void Execute();
}
// Concrete command
public class SimpleCommandLightOn : ICommand
{
private readonly Light light;
public SimpleCommandLightOn(Light light) {
this.light = light;
}
public void Execute() {
light.On();
}
}
// Concrete command
public class SimpleCommandLightOff : ICommand
{
private readonly Light light;
public SimpleCommandLightOff(Light light)
{
this.light = light;
}
public void Execute()
{
light.Off();
}
}
// Invoker
public class SimpleRemoteControl
{
private ICommand command;
public void SetCommand(ICommand command) {
this.command = command;
}
public void OnButtonPress() {
command.Execute();
}
// OffCommand needs to be set
public void OffButtonPress() {
command.Execute();
}
}
In the book they're stating that this is possible:
Light light = new Light();
remote.SetCommand(() => light.On());
However c# throws an error. Is this no the case when working with C#?
Upvotes: 6
Views: 4951
Reputation: 24480
As an alternative, one can create an implementation of the interface that passes the calls through to lambda expressions.
public interface ICommand
{
void Execute();
int Calculate(int input);
}
public class LambdaCommand : ICommand
{
readonly Action execute;
readonly Func<int, int> calculate;
public LambdaCommand(Action execute, Func<int, int> calculate)
{
this.execute = execute;
this.calculate = calculate;
}
public void Execute() => execute();
public int Calculate() => calculate();
}
...
remote.SetCommand(new LambdaCommand(() => light.On(), _ => _));
Upvotes: 0
Reputation: 1799
This does not work because in C#, lambda expressions map to delegates - a concept that does not exist in Java. Java has had a lot of these one-method interfaces for a long time before they finally introduced lambdas in Java 8. Given this and the lack of delegates, it was more or less natural to associate lambdas wit these one-method interfaces. C# / .NET, on the other hand, had delegates from the beginning and used them at many places where you would find a one-method interface in Java. Compare e.g. Observer
and EventHandler
.
So in a .NET API, you would consider using a delegate type like Action
instead of ICommand
, and then you would be able to use a lambda with it.
It should also be noted that .NET does have one-method interfaces, too. Some basic guidance about when to choose an interface or a delegate can be found at MSDN.
Upvotes: 7
Reputation: 63772
Matthias' answer is great, let me just add how a C# idiomatic way of declaring a command would look like:
delegate void Command();
That's it (though nowadays, you'd just use the generic Action
delegate rather than defining your own). Your method would be
public void SetCommand(Command command)
{
this.command = command;
}
and invoking the command is as simple as
command();
Calling the SetCommand
method can look like this:
SetCommand(() => DoSomething()); // Lambda
SetCommand(delegate () { DoSomething(); }); // Anonymous method
SetCommand(Someone.DoSomething); // Named method - Someone can be a type or an instance
As you can see, there's little point in using an interface for something a simple delegate can do. Java uses the syntax it does because it never supported delegates, and because it supports anonymous interface implementations - something C# doesn't have.
As an added bonus, delegates natively support chains - so a delegate can represent a set of delegates that are to be executed in a sequence. This is mostly used in events - another syntax helper that makes your job a little bit easier, with the handy syntax of SomeEvent += someDelegate;
to register an event handler.
Upvotes: 6