I have some OOP code that I am wanting to translate into a functional style, using only immutable types and with only freestanding ( static
) side-effect-free functions.
Here is a much simplified version of one scenario:
abstract class Thing { }
class Sphere : Thing {
public readonly double Radius;
public Sphere(double r) { Radius = r; }
}
class Cube : Thing {
public readonly double Side;
public Cube(double s) { Side = s; }
}
Previously, Thing
defined an abstract method, void Grow(double ratio)
. I have converted the two concrete implementations into freestanding static functions:
Sphere Grow(Sphere s, double ratio) => new Sphere(s.Radius*ratio);
Cube Grow(Cube c, double ratio) => new Cube(c.Side * ratio);
In the OOP version, I could use polymorphism to enumerate over a collection of type Thing
and cause each to grow eg:
things.foreach(x => x.Grow(r));
But how can I do this in the functional approach? I know I can write eg this:
Thing Grow(Thing t, double ratio) => t switch
{
Sphere s => Grow(s, ratio),
Cube c => Grow(c, ratio),
_ => throw new Exception()
};
and
things.Select(t => Grow(t, r))
but I don't like the idea of having to extend the switch expression for each new implementation of Thing that I add.
I can see a way (I think) to do it using reflection, but am not keen on that for other reasons.
Is there a simpler way to achieve the equivalent of OOP polymorphism using functional patterns? (in C#, that is - I know it can be done in eg Haskell).
Try to define and use generic interface for Grow
method like this.
interface IGrowable<T> where T: Thing {
T Grow(T t, double ratio);
}
class Sphere : Thing, IGrowable<Sphere> {
public readonly double Radius;
public Sphere(double r) { Radius = r; }
public Sphere Grow(Sphere s, double ratio) => new Sphere(s.Radius*ratio);
}
class Cube : Thing, IGrowable<Cube> {
public readonly double Side;
public Cube(double s) { Side = s; }
public Cube Grow(Cube c, double ratio) => new Cube(c.Side * ratio);
}
IEnumerable<IGrowable<T>> GrowOwn<T>(IEnumerable<IGrowable<T>> grows, double ratio){
return grows.Select(x => x.Grow(x,ratio));
}
EDIT
If you want to use reflection, define these class
public abstract class Thing {}
public class Sphere : Thing {
public readonly double Radius;
public Sphere(double r) { Radius = r; }
public Sphere(Sphere s, double ratio){ Radius = s.Radius * ratio; }
}
public class Cube : Thing {
public readonly double Side;
public Cube(double s) { Side = s; }
public Cube(Cube c, double ratio){ Side = c.Side * ratio; }
}
and this.
T Grow<T>(T thing, double ratio) where T: Thing => (T)Activator.CreateInstance(typeof(T) , thing, ratio);
You can get Thing
object from this function.
But there is a risk of MissingMethodException
. And reflection is not fast.
( see .NET's design goal , and using Expression Tree might help processing speed)
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.