什么是访问的F#数据识别联合类型在C#中最简单的方法是什么?最简单、类型、方法、数据

2023-09-04 00:28:41 作者:奶茶少冰正常糖

我想了解如何以及C#和F#可以一起玩。我已经采取了一些code从 F#的娱乐和放大器;利润博客它执行基本验证返回歧视union类型:

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&LT; D​​annyTest.Request,串&GT; .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?

推荐答案

与识别联合工作永远不会在不支持模式匹配语言那么简单。然而,你的结果&LT;TSuccess,TFailure&GT; 类型非常简单,应该有一些从C#使用它(如果类型是更复杂的东西,像好方法一名前pression棵树,那么我可能会建议使用Visitor模式)。

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).

其他已经提到的几个选择 - 无论是如何直接访问值以及如何定义匹配方法(如毛的博客文章中所述)。我最喜欢的方法,简单的下游用户是定义 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

这只是增加了扩展 TryGetSuccess TryGetFailure 的回报当值的情况下,并返回歧视工会的情况下(全部)的参数通过退出参数相匹配。 C#的使用是很简单的谁曾经使用任何的TryParse

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).

此外,这为您提供了合理的保障(当它被正确使用),你将只能访问值实际可用时,杜具体情况相匹配。

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.

 
精彩推荐
图片推荐