空F#区分联合案例的C#类型

问题描述 投票:2回答:3

我正在使用C#访问F#区分联合并尝试在联合的情况下使用switch语句。这适用于具有至少一个字段但不包含空值的值,因为这些值没有生成相应的类,只有属性。考虑以下F#区分联合。

type Letter = A of value:int | B of value:string | C | D

在C#中,我在一个函数内部有以下switch语句,该函数具有Letter类型的参数字母:

switch (letter)
{
    case A a: Console.WriteLine(a.value); break;
    case B b: Console.WriteLine(b.value); break;
    default:
        if (letter.IsC) Console.WriteLine("C");
        else if (letter.IsD) Console.WriteLine("D");
}

默认情况处理union值为空的情况。我更喜欢:

switch (letter)
{
    case A a: Console.WriteLine(a.value); break;
    case B b: Console.WriteLine(b.value); break;
    case C c: Console.WriteLine("C"); break;
    case D d: Console.WriteLine("D"); break;
}

但这不起作用,因为类型名称C和D不存在 - C和D是属性而不是类型。我可以通过给C和D一个类型单位的字段来规避这个,但它不是很优雅。为什么仅为非空的区分联合值创建类型,哪种方法最好?

c# f# switch-statement discriminated-union
3个回答
2
投票
switch (letter)
{
    case A a: Console.WriteLine(a.value); break;
    case B b: Console.WriteLine(b.value); break;
    case Letter l when l == C: Console.WriteLine("C"); break;
    case Letter l when l == D: Console.WriteLine("D"); break;
}

空的区分联合使用单例模式和通过构造函数传递的标记,因此属性C被分配给新的Letter(0),D被分配给新的Letter(1),其中Letter是相应的C#类。 case语句的第一部分将始终评估为true,因为letter是Letter类型。 when子句指定该字母必须等于Letter的单例实例,该实例对应于C和D的空的区别联合值。


1
投票

我不认为在使用C#中的F #DU时,猜测为什么F #DU的实现方式与语言规范第8.5.4节“从其他CLI语言使用的联合类型的编译形式”一样非常重要。

这种互操作方案的一个好设计是避免使用“原始”DU,而是将这个实现细节隐藏在F#暴露给其他CLI语言的某些接口背后。

在少数情况下(例如this onethat one),使用来自C#的F#DU的问题在SO上有所涉及,并且建议如何以正确的方式进行。

但是,如果您坚持使用错误的方式使用C#依赖于F#DU实现的细节,那么下面的C#hack将会:

namespace ConsoleApp1
{
    class Program {

        private static void unwindDU(Letter l)
        {
            switch (l.Tag)
            {
                case Letter.Tags.A: Console.WriteLine(((Letter.A)l).value); break;
                case Letter.Tags.B: Console.WriteLine(((Letter.B)l).value); break;
                case Letter.Tags.C: Console.WriteLine("C"); break;
                case Letter.Tags.D: Console.WriteLine("D"); break;
            }
        }

        static void Main(string[] args)
        {
            unwindDU(Letter.NewA(1));
            unwindDU(Letter.C);
        }
    }
}

被执行它将返回

1
C

1
投票

如果您不介意为您的类型添加一点复杂性,您可以定义您的F#类型,如下所示:

type Letter = A of value:int | B of value:string | C of unit | D of unit

完成后,您可以在C#中进行模式匹配,如下所示:

switch (letter)
{
    case A a: Console.WriteLine("A"); break;
    case B b: Console.WriteLine("B"); break;
    case C _: Console.WriteLine("C"); break;
    case D _: Console.WriteLine("D"); break;
}
© www.soinside.com 2019 - 2024. All rights reserved.