Argument validation in F# struct constructor

2020-06-03 02:24发布

Here is a trivial C# struct that does some validation on the ctor argument:

public struct Foo
{
    public string Name { get; private set; }

    public Foo(string name)
        : this()
    {
        Contract.Requires<ArgumentException>(name.StartsWith("A"));
        Name = name;
    }
}

I've managed to translate this into an F# class:

type Foo(name : string) = 
    do 
        Contract.Requires<ArgumentException> (name.StartsWith "A")
    member x.Name = name

However, I can't translate this to a structure in F#:

[<Struct>]
type Foo = 
    val Name : string
    new(name : string) = { do Contract.Requires<ArgumentException> (name.StartsWith "A"); Name = name }

This gives compile errors:

Invalid record, sequence or computation expression. Sequence expressions should be of the form 'seq { ... }'

This is not a valid object construction expression. Explicit object constructors must either call an alternate constructor or initialize all fields of the object and specify a call to a super class constructor.

I've had a look at this and this but they do not cover argument validation.

Where am I doing wrong?

2条回答
做个烂人
2楼-- · 2020-06-03 02:51

If you want to avoid explicit fields (val) and then, two relatively esoteric features, you could use a static Create method and stick to the more common type definition syntax:

[<Struct>]
type Foo private (name: string) = 
  member x.Name = name
  static member Create(name: string) =
    Contract.Requires<ArgumentException> (name.StartsWith "A")
    Foo(name)
查看更多
趁早两清
3楼-- · 2020-06-03 03:05

You can use then block after initializing structs. It is described for classes in your first link in section Executing Side Effects in Constructors, but it works for structs as well.

[<Struct>]
type Foo = 
    val Name : string
    new(name : string) = { Name = name } 
                         then if name.StartsWith("A") then failwith "Haiz"

UPDATE:

Another way closer to your example is to use ; (sequential composition) and parentheses to combine expressions:

[<Struct>]
type Foo = 
    val Name : string
    new(name : string) = 
        { Name = ((if name.StartsWith("A") then failwith "Haiz"); name) } 
查看更多
登录 后发表回答