循环引用和构造函数

Sna*_*ake 9 f# circular-dependency circular-reference

我正在尝试构建一个验证某个类型实例的属性.

为了做到这一点,我必须将其ObjectInstance转换为该类型.

我需要在该类型的成员上设置属性.

所以我们需要求助于and循环定义的关键字.

但是在下面的例子中我得到了错误

自定义属性必须调用对象构造函数

在下面标出的行上.

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
Run Code Online (Sandbox Code Playgroud)

我试着手动调用构造函数,例如:

[<MyAttribute()>]
// or
[<new MyAttribute()>]
Run Code Online (Sandbox Code Playgroud)

但是它们都没有被系统接受.

F#guru可以帮助我吗?

Ant*_*fer 7

有趣的一个.似乎类型推断真的没有那么正确.这里使用的正确语法是[<MyAttribute()>],但尽管您使用了and关键字,但MyAttribute该类尚未知晓.

这是一个解决方法:首先检查要验证的对象是否是正确的类型,然后使用反射来调用验证方法:

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

    override this.IsValid (value: Object, validationContext: ValidationContext) =
        let t = validationContext.ObjectInstance.GetType()
        if t.FullName = "Test.MyClass" then
            let p = t.GetMethod("IsValid")
            if p.Invoke(validationContext.ObjectInstance, [| |]) |> unbox<bool> then
                ValidationResult.Success
            else
                ValidationResult("failed")
        else
            new ValidationResult("No no no")

type MyClass(someValue: int) =
    [<Required>]
    [<Range(1, 7)>]
    [<MyAttribute()>]
    member this.SomeValue = someValue

    member this.IsValid() = someValue <= 7
Run Code Online (Sandbox Code Playgroud)

编辑:为了使其更加清晰,您可以添加一个在验证属性中使用的界面,然后在您的类中实现.

type IIsValid =
    abstract member IsValid: unit -> bool
Run Code Online (Sandbox Code Playgroud)

然后你的IsValid方法就变成了

    override this.IsValid (value: Object, validationContext: ValidationContext) =

        match validationContext.ObjectInstance with
        | :? IIsValid as i -> 
            if i.IsValid() then
                ValidationResult.Success
            else
                ValidationResult("failed")
        | _ ->
            ValidationResult("No no no")
Run Code Online (Sandbox Code Playgroud)

在你的课堂上,这看起来像:

type MyClass(someValue: int) =
    [<Required>]
    [<Range(1, 7)>]
    [<MyAttribute()>]
    member this.SomeValue = someValue

    interface IIsValid with
        member this.IsValid() = someValue <= 7
Run Code Online (Sandbox Code Playgroud)


小智 3

一种解决方案是首先在签名文件中描述您的类型。

由于该属性已在签名文件中指定,因此无需在实现文件中再次添加:

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
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

请参阅https://msdn.microsoft.com/en-us/library/dd233196.aspx以供参考