循环引用和构造函数
Circular reference and constructors
我正在尝试构建一个属性来验证某个类型的特定实例。
为此,我必须将 ObjectInstance
转换为该类型。
并且我需要在该类型的成员上设置属性。
所以我们需要借助and
关键字来进行循环定义。
但是在下面的情况下,我得到的错误是
A custom attribute must invoke an object constructor
在下面标记的行上。
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
我试过手动调用构造函数,如:
[<MyAttribute()>]
// or
[<new MyAttribute()>]
但是系统接受了none个
F# 大师可以帮我解决这个问题吗?
有趣的一个。似乎类型推断真的没有做到这一点。此处使用的正确语法是 [<MyAttribute()>]
,但尽管您使用了 and
关键字,但 MyAttribute
class 尚不清楚。
这里有一个解决方法:首先检查要验证的对象是否确实是正确的类型,然后使用反射调用验证方法:
[<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
编辑:为了使它稍微干净一些,您可以添加一个接口,您在验证属性中使用该接口,然后在您的 class 中实现。
type IIsValid =
abstract member IsValid: unit -> bool
你的 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")
在您的 class 中,它看起来像:
type MyClass(someValue: int) =
[<Required>]
[<Range(1, 7)>]
[<MyAttribute()>]
member this.SomeValue = someValue
interface IIsValid with
member this.IsValid() = someValue <= 7
要摆脱相互递归,您可以做的一件事是将 MyClass
定义分成两部分,并使用类型扩充来添加要用属性标记的成员。
type MyClass(someValue: int) =
member internal this.InternalSomeValue = someValue
type MyAttribute() =
inherit ValidationAttribute()
(* you can refer to MyClass here *)
type MyClass with
[<MyAttribute()>]
member this.SomeValue = this.InternalSomeValue
这更接近您的要求,但我更喜欢界面创意。
一个解决方案是首先在签名文件中描述您的类型。
由于签名文件中已经指定了该属性,所以在实现文件中不需要再添加:
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
我正在尝试构建一个属性来验证某个类型的特定实例。
为此,我必须将 ObjectInstance
转换为该类型。
并且我需要在该类型的成员上设置属性。
所以我们需要借助and
关键字来进行循环定义。
但是在下面的情况下,我得到的错误是
A custom attribute must invoke an object constructor
在下面标记的行上。
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
我试过手动调用构造函数,如:
[<MyAttribute()>]
// or
[<new MyAttribute()>]
但是系统接受了none个
F# 大师可以帮我解决这个问题吗?
有趣的一个。似乎类型推断真的没有做到这一点。此处使用的正确语法是 [<MyAttribute()>]
,但尽管您使用了 and
关键字,但 MyAttribute
class 尚不清楚。
这里有一个解决方法:首先检查要验证的对象是否确实是正确的类型,然后使用反射调用验证方法:
[<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
编辑:为了使它稍微干净一些,您可以添加一个接口,您在验证属性中使用该接口,然后在您的 class 中实现。
type IIsValid =
abstract member IsValid: unit -> bool
你的 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")
在您的 class 中,它看起来像:
type MyClass(someValue: int) =
[<Required>]
[<Range(1, 7)>]
[<MyAttribute()>]
member this.SomeValue = someValue
interface IIsValid with
member this.IsValid() = someValue <= 7
要摆脱相互递归,您可以做的一件事是将 MyClass
定义分成两部分,并使用类型扩充来添加要用属性标记的成员。
type MyClass(someValue: int) =
member internal this.InternalSomeValue = someValue
type MyAttribute() =
inherit ValidationAttribute()
(* you can refer to MyClass here *)
type MyClass with
[<MyAttribute()>]
member this.SomeValue = this.InternalSomeValue
这更接近您的要求,但我更喜欢界面创意。
一个解决方案是首先在签名文件中描述您的类型。
由于签名文件中已经指定了该属性,所以在实现文件中不需要再添加:
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