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

时间:2014-05-24 08:56:16

标签: c# f# c#-to-f#

使用C#中的F#Disrediminion 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来检查对象是否属于该类型; if且仅当它是,它可以转换为Shape.XXXX类,并且只有它的项才可访问; Shape.XXXX类的构造函数是内部的,即无法访问。

是否有人知道从歧视联盟获取数据的更简单选项?

3 个答案:

答案 0 :(得分:12)

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

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

如果你真的需要将这两个案例公开给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
}

答案 1 :(得分:5)

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

  • .IsC...

  • .Tag(为每个案例提供一个整数标记)

  • .Item(在获取数据的子类型上 - 仅当存在多个联合案例时才会出现此情况)

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

答案 2 :(得分:4)

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

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

在F#中,我们可以在Shape类型上使用模式匹配:

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