F # Дискриминация использования Союза с С#
Каковы наилучшие способы использования F # Discriminated Unions из С#?
Я некоторое время копался в этой проблеме, я, вероятно, нашел самый простой способ, но, поскольку он довольно сложный, может быть и другое, чего я не вижу...
Наличие дискриминационного объединения, например:
type Shape =
| Rectangle of float * float
| Circle of float
использование С#, которое я нашел, будет (избегая использования vars, чтобы сделать тип очевидным):
Shape circle = Shape.NewCircle(5.0);
if (circle.IsCircle)
{
Shape.Circle c = (Shape.Circle)circle;
double radius = c.Item;
}
В С# статические методы NewXXXX
всегда создают объект класса Shape
, также существует метод IsXXXX
для проверки того, является ли объект типом; если и только если "да", то он поддается классу Shape.XXXX
, и только тогда его элементы доступны; конструктор классов Shape.XXXX
является внутренним, то есть недоступным.
Кто-нибудь знает о более простой опции для получения данных от дискриминационного объединения?
Ответы
Ответ 1
Если вы пишете библиотеку в F #, которая предоставляется разработчикам С#, разработчики С# должны иметь возможность использовать ее, не зная ничего о F # (и не зная, что она написана в F #). Это также рекомендуется Руководством по дизайну F #.
Для дискриминационных союзов это сложно, потому что они следуют различным принципам дизайна, чем С#. Поэтому я, вероятно, скрою всю функциональность обработки (например, вычислительную область) в коде F # и выставляю ее как обычные члены.
Если вам действительно нужно разоблачить два случая для разработчиков С#, то я думаю, что что-то вроде этого является достойным вариантом для простого дискриминационного объединения:
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
В С# вы можете использовать его так же, как знакомые методы TryParse
:
int w, h, r;
if (shape.TryRectangle(out w, out h)) {
// Code for rectangle
} else if (shape.TryCircle(out r)) {
// Code for circle
}
Ответ 2
В соответствии с спецификацией F # единственный доступный доступ к нему осуществляется через следующие методы экземпляра
Тем не менее, вы можете свободно писать методы в F #, чтобы упростить взаимодействие.
Ответ 3
Предполагая, что нам нужно вычислить площадь каждой фигуры полиморфно.
В С# мы обычно создаем гипотетическую иерархию объектов и Visitor. В этом примере нам нужно будет создать класс ShapeVisitor, а затем производный класс посетителей ShapeAreaCalculator.
В F # мы можем использовать Match Matching для типа 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