简体   繁体   English

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

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

I'm accessing an F# discriminated union using C# and trying to use a switch statement over the union's cases. 我正在使用C#访问F#区分联合并尝试在联合的情况下使用switch语句。 This works fine for values that have at least one field but not for empty values as these don't have a corresponding class generated, only a property. 这适用于具有至少一个字段但不包含空值的值,因为这些值没有生成相应的类,只有属性。 Consider the following F# discriminated union. 考虑以下F#区分联合。

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

In C# I have the following switch statement inside a function that has an argument letter of type Letter: 在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");
}

The default case handles the cases where the union value is empty. 默认情况处理union值为空的情况。 I'd much prefer: 我更喜欢:

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

But this doesn't work because the type names C and D do not exist - C and D are properties not types. 但这不起作用,因为类型名称C和D不存在 - C和D是属性而不是类型。 I can circumvent this by giving C and D a field of type unit but it's not very elegant. 我可以通过给C和D一个类型单位的字段来规避这个,但它不是很优雅。 Why are types only created for non-empty discriminated union values and what's the best workaround? 为什么仅为非空的区分联合值创建类型,哪种方法最好?

I do not think that guessing why F# DUs were implemented the way the Language spec part 8.5.4 Compiled Form of Union Types for Use from Other CLI Languages prescribed would be utterly important when using F# DUs from C#. 我不认为在使用C#中的F #DU时,猜测为什么F #DU的实现方式与语言规范第8.5.4节“从其他CLI语言使用的联合类型的编译形式”一样非常重要。

A good design for such interop scenario would be avoid using "raw" DU, hiding instead this implementation detail behind some interface that F# would expose to other CLI languages. 这种互操作方案的一个好设计是避免使用“原始”DU,而是将这个实现细节隐藏在F#暴露给其他CLI语言的某些接口背后。

On few occasions (eg this one and that one ) the matter with use of F# DU from C# was covered on SO and recommendations were given on how to do it the right way . 在少数情况下(例如这一个那个 ),使用来自C#的F#DU的问题在SO上得到了解决,并且提出了如何以正确的方式进行的建议

But if you would insist on a wrong way with your C# relying upon the specifics of F# DU implementation, the following C# hack will do: 但是,如果您坚持使用错误的方式使用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);
        }
    }
}

Being executed it will return 被执行它将返回

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

Empty discriminated unions use the singleton pattern with the tag passed through the constructor so the property C is assigned to new Letter(0) and D to new Letter(1) where Letter is the corresponding C# class. 空的区分联合使用单例模式和通过构造函数传递的标记,因此属性C被分配给新的Letter(0),D被分配给新的Letter(1),其中Letter是相应的C#类。 The first part of the case statement will always evaluate to true as letter is of type Letter. case语句的第一部分将始终评估为true,因为letter是Letter类型。 The when clauses specify that the letter must be equal to the singleton instance of Letter that corresponds to the empty discriminated union values of C and D. when子句指定该字母必须等于Letter的单例实例,该实例对应于C和D的空的区别联合值。

If you don't mind adding a little bit of complexity to your type you can define your F# type like so: 如果您不介意为您的类型添加一点复杂性,您可以定义您的F#类型,如下所示:

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

Having done that, you can pattern match in C# as follows: 完成后,您可以在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