Friday 3 January 2020

Polymorphic this Types

The other day I came across for the first time with Polymorphic this types. I would say that I've never needed this feature so far (I think I've never designed a fluent interface, at least in a static language), but the sample is pretty clear of how useful it can be when you're using a language where types bring you security but also restrictions. What we get here is that when we are in a derived class and invoke a method of the parent class that returns "this", the compiler understands that such "this" is now an instance of the derived class, not just of the parent class. Notice that in the method signature you have to specify that you return "this":
public add(operand: number): this {
(the typescript compiler won't apply this feature just by seeing that you are doing "return this" at the end of the method.

This said, and given that C# is missing this feature, how can we make a fluent API like the one in the sample work? Using just a simple "casting" with as quickly becomes pretty dirty as we need to do more and more "castings":

 v = (new ScientificCalculator(2)
   .Multiply(5) as ScientificCalculator)
   .Sin()
   .Add(1);
 Console.WriteLine(v.CurrentValue());
 
 //starts to look uglier as we start to add more and more initial parenthesis
 v = ((new ScientificCalculator(2)
   .Multiply(5) as ScientificCalculator)
   .Sin()
   .Add(1) as ScientificCalculator)
   .Sin();
 Console.WriteLine(v.CurrentValue());

A solution that occurred to me and that I think looks a bit better is wrapping the as operator into a generic chainable method. Thanks to Extension methods we can do something like this:

 static class Extensions
    {
        public static T AsType<T>(this object obj) where T: class
        {
            return obj as T;
        }
    }

 v = new ScientificCalculator(2)
   .Multiply(5).AsType<ScientificCalculator>()
   .Sin()
   .Add(1);
 Console.WriteLine(v.CurrentValue());


 v = new ScientificCalculator(2)
   .Multiply(5).AsType<ScientificCalculator>()
   .Sin()
   .Add(1).AsType<ScientificCalculator>()
   .Sin();


No comments:

Post a Comment