F#从C#中区分联盟用法

使用C#中的F#Discriminated Unions的最佳方法是什么?

我一直在深入研究这个问题,我可能找到了最简单的方法,但由于它相当复杂,可能还有其他一些我看不到的东西……

有一个有区别的联盟,例如:

type Shape = | Rectangle of float * float | Circle of float 

我发现C#的用法是(避免使用变量,使类型明显):

 Shape circle = Shape.NewCircle(5.0); if (circle.IsCircle) { Shape.Circle c = (Shape.Circle)circle; double radius = c.Item; } 

在C#中, NewXXXX静态方法总是创建Shape类的对象,还有一个方法IsXXXX来检查对象是否属于该类型; 当且仅当它是,它可以转换为Shape.XXXX类,并且只有它的项才可访问; Shape.XXXX类的构造函数是内部的,即Shape.XXXX访问。

有人知道从一个受歧视的联盟获取数据的更简单的选择吗?

如果您正在使用F#编写一个向C#开发人员公开的库,那么C#开发人员应该能够在不了解F#的情况下使用它(并且不知道它是用F#编写的)。 F#设计指南也推荐这一点。

对于受歧视的工会,这很棘手,因为它们遵循与C#不同的设计原则。 因此,我可能会隐藏F#代码中的所有处理function(如计算区域)并将其作为普通成员公开。

如果你真的需要将这两个案例暴露给C#开发人员,那么我觉得像这样的东西对于一个简单的歧视联盟来说是一个不错的选择:

 type Shape = | Rectangle of float * float | Circle of float member x.TryRectangle(width:float byref, height:float byref) = match x with | Rectangle(w, h) -> width <- w; height <- h; true | _ -> false member x.TryCircle(radius:float byref) = match x with | Circle(r) -> radius <- r; true | _ -> false 

在C#中,您可以像熟悉的TryParse方法一样使用它:

 int w, h, r; if (shape.TryRectangle(out w, out h)) { // Code for rectangle } else if (shape.TryCircle(out r)) { // Code for circle } 

根据F#规范,唯一可用的互操作是通过以下实例方法

  • .IsC...

  • .Tag (给每个案例一个整数标签)

  • .Item (在子类型上获取数据 – 只有在存在多个联合情况时才会出现)

但是,您可以自由地在F#中编写方法以使interop更容易。

假设我们需要以多态方式计算每个Shape的面积

在C#中,我们通常会创建一个假设的对象层次结构和一个访问者 。 在这个例子中,我们必须创建一个ShapeVisitor类,然后创建一个派生的ShapeAreaCalculator访问者类。

在F#中,我们可以在Shape类型上使用Pattern Matching:

 let rectangle = Rectangle(1.3, 10.0) let circle = Circle (1.0) let calculateArea shape = match shape with | Circle radius -> 3.141592654 * radius * radius | Rectangle (height, width) -> height * width let rectangleArea = calculateArea(rectangle) // -> 1.3 * 10.0 let circleArea = calculateArea(circle) // -> 3.141592654 * 1.0 * 1.0