Search code examples
f#circular-dependencycircular-reference

Circular reference and constructors


I'm trying to build an Attribute that validates a certain instance of a type.

In order to do this I have to cast the ObjectInstance to that type.

And I need to set the attribute on the member of that type.

So we need to resort to the and keyword for the circular definition.

However in the following case I get the error that

A custom attribute must invoke an object constructor

On the line marked below.

namespace Test

open System
open System.ComponentModel.DataAnnotations

[<AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = true)>]
type MyAttribute() =
    class
    inherit ValidationAttribute ()

    override this.IsValid (value: Object, validationContext: ValidationContext) =
        match validationContext.ObjectInstance with
        | :? MyClass as item ->
            // TODO more validation
            ValidationResult.Success
        | _ ->
            new ValidationResult("No no no")
    end
and MyClass(someValue) =
    [<Required>]
    [<Range(1, 7)>]
  //vvvvvvvvvvvvvvv
    [<MyAttribute>]
  //^^^^^^^^^^^^^^^
    member this.SomeValue : int = someValue

I tried manually invoking the constructor, such as:

[<MyAttribute()>]
// or
[<new MyAttribute()>]

But none of them are accepted by the system.

Can an F# guru help me out here?


Solution

  • One solution would be to first describe your types in a signature files.

    Since the attribute is specified in the signature file, you don't need to add it again in the implementation file:

    Foo.fsi:

    namespace Foo
    
    open System
    
    [<AttributeUsage(AttributeTargets.Property)>]
    type MyAttribute =
        inherit System.Attribute
    
        new : unit -> MyAttribute
    
        member Foo : unit -> MyClass
    
    and MyClass =
        new : someValue : int -> MyClass
    
        [<MyAttribute()>]
        member SomeValue : int
    

    Foo.fs:

    namespace Foo
    
    open System
    
    [<AttributeUsage(AttributeTargets.Property)>]
    type MyAttribute() =
        inherit Attribute()
    
        member this.Foo () =
            new MyClass(1)
    
    and MyClass(someValue) =
        // [<MyAttribute()>] -> specified in the fsi, still appears in compiled code
        member this.SomeValue : int = someValue
    

    See https://msdn.microsoft.com/en-us/library/dd233196.aspx for reference