繁体   English   中英

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

[英]C# types for empty F# discriminated union cases

我正在使用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 #DU时,猜测为什么F #DU的实现方式与语言规范第8.5.4节“从其他CLI语言使用的联合类型的编译形式”一样非常重要。

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

在少数情况下(例如这一个那个 ),使用来自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
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的空的区别联合值。

如果您不介意为您的类型添加一点复杂性,您可以定义您的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;
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM