【问题标题】:C# types for empty F# discriminated union cases空 F# 可区分联合案例的 C# 类型
【发布时间】:2018-09-01 00:29:05
【问题描述】:

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

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

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

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");
}

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

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 一个 unit 类型的字段来规避这个问题,但这不是很优雅。为什么只为非空的可区分联合值创建类型,最好的解决方法是什么?

【问题讨论】:

标签: c# f# switch-statement discriminated-union


【解决方案1】:

我不认为在使用 F# DU 时,猜测为什么 F# DU 以语言规范部分 8.5.4 用于其他 CLI 语言的联合类型的编译形式的方式实现是非常重要的来自 C#。

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

在少数情况下(例如 this onethat one)SO 中涵盖了使用 C# 中的 F# DU 的问题,并就如何以正确的方式进行操作提出了建议

但如果你坚持错误的方式你的 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

【讨论】:

    【解决方案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 分配给 new Letter(0) 和 D 分配给 new Letter(1),其中 Letter 是相应的 C# 类。 case 语句的第一部分将始终评估为 true,因为 letter 是 Letter 类型。 when 子句指定字母必须等于 Letter 的单例实例,该实例对应于 C 和 D 的空可区分联合值。

    【讨论】:

    【解决方案3】:

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

    【讨论】:

    • 问题中提到了这种可能性。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-08-04
    • 1970-01-01
    • 2017-11-18
    • 2011-11-12
    • 2017-05-06
    • 2020-08-22
    相关资源
    最近更新 更多