【问题标题】:C# How to implement polymorphism in a functional style?C#如何以函数式风格实现多态性?
【发布时间】:2020-10-04 00:04:40
【问题描述】:

我有一些 OOP 代码,我想将其转换为函数式风格,仅使用不可变类型并且仅使用独立 (static) 无副作用函数。

这是一个场景的非常简化的版本:

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; }
}

之前,Thing 定义了一个抽象方法 void Grow(double ratio)。我已将这两个具体实现转换为独立的静态函数:

Sphere Grow(Sphere s, double ratio) => new Sphere(s.Radius*ratio);
Cube Grow(Cube c, double ratio) => new Cube(c.Side * ratio);

在 OOP 版本中,我可以使用多态性来枚举类型为 Thing 的集合并导致每个集合增长,例如:

things.foreach(x => x.Grow(r));

但是我怎样才能在函数式方法中做到这一点?我知道我可以写例如这个:

Thing Grow(Thing t, double ratio) => t switch
{
     Sphere s => Grow(s, ratio),
     Cube c => Grow(c, ratio),
     _ => throw new Exception()
};

things.Select(t => Grow(t, r))

但我不喜欢必须为我添加的每个新的 Thing 实现扩展 switch 表达式的想法。

我可以看到一种方法(我认为)使用反射来做到这一点,但出于其他原因我并不热衷于此。

有没有更简单的方法来使用函数模式来实现等效的 OOP 多态性? (在 C# 中,即 - 我知道它可以在例如 Haskell 中完成)。

【问题讨论】:

  • 如果您展示了 “假设我有...”示例的代码,并向我们展示了您认为想要达到的目标(作为代码),人们可以跟随你的想法。当问题有代码时,此站点的工作效果好多了。对于它的价值,在我的上一份工作中,有人创建了一个功能编程的服务,就像你描述的那样。这是一头可怕的野兽,在维护期间非常脆弱(部分原因是您遇到的问题)
  • 我尽量不要让帖子太长。但我现在已经编辑以提供一个更具体的例子。注:这是一个很好的简化:我不是在寻求仅解决这个特定简化示例的答案,而是关于多态性的一般要点。
  • 你的观点有点偏向于 OOP:expression problem。您正在寻找类型类多态性,这基本上是临时多态性,其中重载名称到类型的映射本身就是给定的名称。然后,您可以关联不同的重载名称,为它们分配规律并分层排列它们。我认为表达问题可以通过一种称为最终无标签编码的更高级的功能技术来解决,但这超出了我的经验。

标签: c# reflection functional-programming polymorphism


【解决方案1】:

尝试像这样为Grow方法定义和使用通用接口。

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));
}

编辑

如果你想使用反射,定义这些类

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; }
}

还有这个。

T Grow<T>(T thing, double ratio) where T: Thing => (T)Activator.CreateInstance(typeof(T) , thing,  ratio);

你可以通过这个函数获得Thing对象。
但是有MissingMethodException的风险。而且反射并不快。
see .NET's design goal,使用表达式树可能有助于提高处理速度)

【讨论】:

  • 1.什么是“事物”定义为 - 它不能是 List>?
  • 2.如果我失去了返回自己的类型的要求,而只返回一个事物,那么这可以工作 - 但它实际上只是回到 OOP 即类具有实例方法,尽管它们现在没有副作用。跨度>
猜你喜欢
  • 1970-01-01
  • 2021-12-13
  • 1970-01-01
  • 2011-06-25
  • 1970-01-01
  • 2010-10-06
  • 2014-12-03
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多