如何建立符合业务规则的F#类型?

我正在尝试在F#中构建一个类型,当我得到该类型的对象时,可以确保它处于有效状态。
该类型称为JobId,它仅包含一个Guid
业务规则是:它必须是Guid,但不能为空Guid。
我已经在C#中实现了该类型,但是现在我想将其移植到F#类库中。

这是C#类型:

public sealed class JobId
{
    public string Value { get; }

    private JobId(string value)
        => Value = value;

    public static JobId Create()
        => new JobId(Guid.NewGuid().ToString("N"));

    public static Option<JobId> Create(Guid id)
        => id == Guid.Empty
        ? None
        : Some(new JobId(id.ToString("N"));

    public static Option<JobId> Create(string id)
    {
        try
        {
            var guid = new Guid(id);
            return Create(guid);
        }
        catch (FormatException)
        {
            return None;
        }
    }
}

那么我该如何在F#中构建它?谢谢!

更新1:
我试图将其实现为可区分的联合类型,如下所示:

type JobId =
    | JobId of string

但是问题是,我无法用这种方法定义任何业务规则。
因此,最后一个问题是:如何确保string中的JobId 特定格式?

chaoyangxx2003 回答:如何建立符合业务规则的F#类型?

我已经修改了Tomas的答案,以使用DU而不是类来保留适当的相等性和比较性,例如,允许JobId作为分组键像预期的那样工作。

[<AutoOpen>]
module JobId =
    open System
    type JobId = private JobId of string with
        static member Create() = JobId(Guid.NewGuid().ToString("N"))

        static member Create(id:Guid) =
            if id = Guid.Empty then None
            else Some(JobId(id.ToString("N")))

        static member Create(id:string) =
            try JobId.Create(Guid(id))
            with :? FormatException -> None

必须将类型放入模块中,然后才能直接在该模块外部访问DU构造函数:

JobId.Create (System.Guid.NewGuid()) // Some (JobId "1715d4ae776d441da357f0efb330be43")
JobId.Create System.Guid.Empty // None
JobId System.Guid.Empty // Compile error
,

已区分的并集和F#记录使内部表示形式公开,因此仅在内部表示形式的所有值均有效的情况下才有效。如果需要定义进行一些检查的基本类型,则需要隐藏其内部的类型。在这种情况下,我将直接使用与您的C#代码相当的F#代码:

type JobId private (id:string) = 
  member x.Value = id 
  static member Create() =
    JobId(Guid.NewGuid().ToString("N"))

  static member Create(id:Guid) =
    if id = Guid.Empty then None
    else Some(new JobId(id.ToString("N")))

  static member Create(id:string) =
    try JobId.Create(Guid(id))
    with :? FormatException -> None

请注意,有两种情况需要防范-一种是string值,实际上不是Guid,另一种是空的Guid。您可以使用类型系统来防止出现第一种情况-只需创建一个值为Guid而不是string的DU!

type JobId = 
  | JobId of Guid

A,无法确保此guid不为空。但是,比上述更好的解决方案可能是定义NonEmptyGuid(使用类似上述的类),以仅表示非空guid。那么您的域模型可能是:

type JobId = 
  | JobId of NonEmptyGuid

如果您在项目中的其他地方使用NonEmptyGuid,则特别好。

本文链接:https://www.f2er.com/3058351.html

大家都在问