In the previous posts (here and here) we’ve explored a couple of options of how to implement the strategy design pattern. The one thing which kept coming back to me though is that it’s not generic. In this post we’ll explorer implementing the first iteration using generics with the new C# 7.3 feature.
But does it need to be generic?
In this example it doesn’t need to be generic. However the whole point of design patterns is to make code clear, clean and maintainable. If we can have interfaces which make implementations consistent then this can help with readable and maintainable code. This example is just that; an example.
Brief History
Since the publishing of generics in C# in the .NET 2 timeframe, circa 2005, there were restrictions of what types you could apply as a generic constraint. These restrictions stopped, amongst others, using the Enum
type as a constraint. As of C# 7.3 this restriction has been removed.
Updating the example
In the example I am working with it wouldn’t make sense to remove the Operator
enum from the interface definition however this is to show an example of using an Enum
as a generic constraint.
Using an Enum
constraint the IMathOperator
interface can be updated in the following way.
Before:
public interface IMathOperator
{
Operator Operator { get; }
int Calculate(int a, int b);
}
After:
public interface IMathOperator<out T> where T : Enum
{
T Operator { get; }
int Calculate(int a, int b);
}
Comparing the two we can see there is minimal difference except the generic specification. The generic parameter is defined that the type which can be specified will be covarient
as defined by the out
keyword more info can be found here.
You will also notice that the generic parameter has a constraint on it which limits what T
can be. In this case this has been restricted by the new functionality T : Enum
.
Updating the Operators
Once the interface has been updated the implementations need updating as well. Due to the way it has been designed this has minimal changes for the implementations. For example, let’s look at the AddOperator
.
Before:
public class AddOperator : IMathOperator
{
public Operator Operator => Operator.Add;
public int Calculate(int a, int b) => a + b;
}
After:
public class AddOperator : IMathOperator<Operator>
{
public Operator Operator => Operator.Add;
public int Calculate(int a, int b) => a + b;
}
DI Registration
Due to the change in the interface definition the registrations will also have to be updated when added to the IServiceCollection
in the ConfigureServices
method.
services.AddScoped<IMathStrategy, MathStrategy>();
services.AddScoped<IMathOperator<Operator>, AddOperator>();
Conclusion
In this post we have looked at how we can update the IMathOperator
interface to add in a generic parameter and now using C# 7.3 specify that the generic parameter must be an Enum
.
A full working example can be found on Github.
Any questions/comments then please contact me on Twitter @WestDiscGolf