Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/fsharp/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
空F#区分工会案例的C#类型_C#_F#_Switch Statement_Discriminated Union - Fatal编程技术网

空F#区分工会案例的C#类型

空F#区分工会案例的C#类型,c#,f#,switch-statement,discriminated-union,C#,F#,Switch Statement,Discriminated Union,我正在使用C访问一个F#歧视工会,并试图在工会的案例中使用switch语句。这适用于至少有一个字段的值,但不适用于空值,因为这些值没有生成相应的类,只有一个属性。考虑下面的F判别判别式, type Letter = A of value:int | B of value:string | C | D 在C#中,我在一个函数中有以下switch语句,该函数的参数字母类型为字母: switch (letter) { case A a: Console.WriteLine(a.value);

我正在使用C访问一个F#歧视工会,并试图在工会的案例中使用switch语句。这适用于至少有一个字段的值,但不适用于空值,因为这些值没有生成相应的类,只有一个属性。考虑下面的F判别判别式,

type Letter = A of value:int | B of value:string | C | D
在C#中,我在一个函数中有以下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");
}
默认情况处理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一个类型为unit的字段来避免这个问题,但它不是很优雅。为什么只为非空的有区别的联合值创建类型?最好的解决方法是什么?

我不认为,在使用C语言中的F#DUs时,猜测为什么F#DUs是按照语言规范第8.5.4部分中规定的联合类型编译形式实现的,这一点是非常重要的

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

在少数情况下(例如和),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

空的判别联合使用单例模式,标记通过构造函数传递,因此属性C被分配给新字母(0),属性D被分配给新字母(1),其中字母是对应的C#类。case语句的第一部分将始终计算为true,因为letter是letter类型。when子句指定字母必须等于字母的单例实例,该实例对应于空的区分并集值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;
}

您是否尝试过将F#编译成程序集,然后反编译成C#?谢谢您的建议。我看了一下F#反编译成C#here(),这导致了我下面给出的答案。问题中提到了这种可能性。我在这里写了几个选项:如果这个问题中出现了新的选项,我将添加更多选项。
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;
}