我正在使用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");
}
默认情况下处理联合值为空的情况。我更喜欢:
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的字段来规避这一点,但这不是很优雅。为什么只为非空的已区分联合值创建类型,最好的解决方法是什么?
答案 0 :(得分: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 :(得分:1)
我认为猜测使用F#DU时为什么要按规定的语言规范部分 8.5.4编译为其他CLI语言使用的联合类型的形式实施F#DU绝对不重要来自C#。
针对这种互操作方案的一个好的设计是避免使用“原始” DU,而将实现细节隐藏在F#会暴露给其他CLI语言的某些接口后面。
在少数情况下(例如this one和that 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 :(得分: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;
}