F# Discriminated Union usage from C# [duplicate]

不打扰是莪最后的温柔 提交于 2019-11-27 14:53:43

问题


What are the best ways to use F# Discriminated Unions from C#?

I have been digging into this problem for a while, I have probably found the simplest way, but as it is rather complex, there may be some other thing I don't see...

Having a discriminated union, e.g.:

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

the usage from C# I found would be (avoiding using vars, to make the type obvious):

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

In C#, the NewXXXX static methods always create object of the Shape class, there is also a method IsXXXX to check if the object is of the type; if and only if yes, it is castable to the Shape.XXXX class, and only then its items are accessible; constructor of the Shape.XXXX classes are internal, i.e. unaccessible.

Is anyone aware of a simpler option to get the data from a discriminated union?


回答1:


If you are writing a library in F# that is exposed to C# developers, then C# developers should be able to use it without knowing anything about F# (and without knowing that it was written in F#). This is also recommended by F# design guidelines.

For discriminated unions, this is tricky, because they follow different design principles than C#. So, I would probably hide all processing functionality (like calculating area) in the F# code and expose it as ordinary members.

If you really need to expose the two cases to C# developers, then I think something like this is a decent option for a simple discriminated union:

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

In C#, you can use it in the same way as the familiar TryParse methods:

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



回答2:


According to the F# spec, the only interop available is through the following instance methods

  • .IsC...

  • .Tag (which gives an integer tag to each case)

  • .Item (on the subtypes to get the data - this is only present when there is more than one union case)

However, you are free to write methods in the F# to make the interop easier.




回答3:


Assuming that we need to calculate the area of each Shape polymorphically.

In C# we would normally create a hypothetical object hierarchy and a Visitor. In this example, we would have to create a ShapeVisitor class and then a derived ShapeAreaCalculator visitor class.

In F#, we can use Pattern Matching on the Shape type:

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


来源:https://stackoverflow.com/questions/23843142/f-discriminated-union-usage-from-c-sharp

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!