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可以帮助我吗?
有趣的一个.似乎类型推断真的没有那么正确.这里使用的正确语法是[<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以供参考