在 C# 中访问 F# 可区分联合类型的数据的最简单方法是什么? [英] What is the simplest way to access data of an F# discriminated union type in C#?

查看:17
本文介绍了在 C# 中访问 F# 可区分联合类型的数据的最简单方法是什么?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试了解 C# 和 F# 的配合效果如何.我从 F# for Fun & 中获取了一些代码.利润博客,它执行基本验证并返回可区分的联合类型:

I'm trying to understand how well C# and F# can play together. I've taken some code from the F# for Fun & Profit blog which performs basic validation returning a discriminated union type:

type Result<'TSuccess,'TFailure> = 
    | Success of 'TSuccess
    | Failure of 'TFailure

type Request = {name:string; email:string}

let TestValidate input =
    if input.name = "" then Failure "Name must not be blank"
    else Success input

当试图在 C# 中使用它时;我可以找到访问针对成功和失败的值(失败是一个字符串,成功是再次请求)的唯一方法是使用大量令人讨厌的强制转换(这是很多输入,并且需要输入我期望的实际类型)在元数据中推断或可用):

When trying to consume this in C#; the only way I can find to access the values against Success and Failure (failure is a string, success is the request again) is with big nasty casts (which is a lot of typing, and requires typing actual types that I would expect to be inferred or available in the metadata):

var req = new DannyTest.Request("Danny", "fsfs");
var res = FSharpLib.DannyTest.TestValidate(req);

if (res.IsSuccess)
{
    Console.WriteLine("Success");
    var result = ((DannyTest.Result<DannyTest.Request, string>.Success)res).Item;
    // Result is the Request (as returned for Success)
    Console.WriteLine(result.email);
    Console.WriteLine(result.name);
}

if (res.IsFailure)
{
    Console.WriteLine("Failure");
    var result = ((DannyTest.Result<DannyTest.Request, string>.Failure)res).Item;
    // Result is a string (as returned for Failure)
    Console.WriteLine(result);
}

有没有更好的方法来做到这一点?即使我必须手动转换(可能会出现运行时错误),我也希望至少缩短对类型的访问(DannyTest.Result.Failure).有没有更好的办法?

Is there a better way of doing this? Even if I have to manually cast (with the possibility of a runtime error), I would hope to at least shorten access to the types (DannyTest.Result<DannyTest.Request, string>.Failure). Is there a better way?

推荐答案

在不支持模式匹配的语言中,使用可区分的联合永远不会那么简单.但是,您的 Result<'TSuccess, 'TFailure> 类型足够简单,应该有一些不错的方法可以从 C# 中使用它(如果类型更复杂,例如表达式树,那么我可能会建议使用访问者模式).

Working with discriminated unions is never going to be as straightforward in a language that does not support pattern matching. However, your Result<'TSuccess, 'TFailure> type is simple enough that there should be some nice way to use it from C# (if the type was something more complicated, like an expression tree, then I would probably suggest to use the Visitor pattern).

其他人已经提到了一些选项 - 如何直接访问值以及如何定义 Match 方法(如 Mauricio 的博客文章中所述).我最喜欢的简单 DU 方法是定义 TryGetXyz 方法,这些方法遵循与 Int32.TryParse 相同的风格 - 这也保证了 C# 开发人员将熟悉该模式.F# 定义如下所示:

Others already mentioned a few options - both how to access the values directly and how to define Match method (as described in Mauricio's blog post). My favourite method for simple DUs is to define TryGetXyz methods that follow the same style of Int32.TryParse - this also guarantees that C# developers will be familiar with the pattern. The F# definition looks like this:

open System.Runtime.InteropServices

type Result<'TSuccess,'TFailure> = 
    | Success of 'TSuccess
    | Failure of 'TFailure

type Result<'TSuccess, 'TFailure> with
  member x.TryGetSuccess([<Out>] success:byref<'TSuccess>) =
    match x with
    | Success value -> success <- value; true
    | _ -> false
  member x.TryGetFailure([<Out>] failure:byref<'TFailure>) =
    match x with
    | Failure value -> failure <- value; true
    | _ -> false

这只是添加了扩展 TryGetSuccessTryGetFailure,当值与大小写匹配时返回 true 并返回被区分联合的(所有)参数case 通过 out 参数.对于曾经使用过 TryParse 的人来说,C# 的使用非常简单:

This simply adds extensions TryGetSuccess and TryGetFailure that return true when the value matches the case and return (all) parameters of the discriminated union case via out parameters. The C# use is quite straightforward for anyone who has ever used TryParse:

  int succ;
  string fail;

  if (res.TryGetSuccess(out succ)) {
    Console.WriteLine("Success: {0}", succ);
  }
  else if (res.TryGetFailure(out fail)) {
    Console.WriteLine("Failuere: {0}", fail);
  }

我认为熟悉这种模式是最重要的好处.当您使用 F# 并向 C# 开发人员公开其类型时,您应该以最直接的方式公开它们(C# 用户不应认为 F# 中定义的类型在任何方面都是非标准的).

I think the familiarity of this pattern is the most important benefit. When you use F# and expose its type to C# developers, you should expose them in the most direct way (the C# users should not think that the types defined in F# are non-standard in any way).

此外,这为您提供了合理的保证(当它被正确使用时)您只会访问当 DU 匹配特定情况时实际可用的值.

Also, this gives you reasonable guarantees (when it is used correctly) that you will only access values that are actually available when the DU matches a specific case.

这篇关于在 C# 中访问 F# 可区分联合类型的数据的最简单方法是什么?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆