F# "Code is not sufficiently generic" 实现接口时“^T 无法泛化”
F# "Code is not sufficiently generic" "^T could not be generalized" when implementing interface
我正在尝试在一个项目中从 Giraffe.Serialization.Json 实现 IJsonSerializer 以使用 Microsoft.FSharpLu.Json,但是我在使用其中一种通用方法(下面的代码)时遇到了问题
type FSharpLuSerializer () =
interface Giraffe.Serialization.Json.IJsonSerializer with
member __.Deserialize<'T> (json : string) =
Microsoft.FSharpLu.Json.Default.deserialize<'T> json
我遇到错误
This code is not sufficiently generic.
The type variable ^T could not be generalized because it would escape its scope
我看到其他问题也有同样的错误,但我不确定如何将他们的解决方案应用到我的情况中。我想这与使用 ^T 的 FSharpLu.Json 有关,但我不知道我的解决方法是什么
https://github.com/Microsoft/fsharplu/blob/master/FSharpLu.Json/Default.fs
这是 Giraffe IJSonSerializer 接口
[<AllowNullLiteral>]
type IJsonSerializer =
abstract member SerializeToString<'T> : 'T -> string
abstract member SerializeToBytes<'T> : 'T -> byte array
abstract member SerializeToStreamAsync<'T> : 'T -> Stream -> Task
abstract member Deserialize<'T> : string -> 'T
abstract member Deserialize<'T> : byte[] -> 'T
abstract member DeserializeAsync<'T> : Stream -> Task<'T>
这是一个混乱的问题,一般来说,接口需要通过方法上的泛型来捕获要应用的类型,更复杂的是,你的 FSharpLu.Json 库正在使用内联 SRTP,SRTP 内联模板方法对 ^ 约束类型的调用,因此在编译后的代码中,每次调用都有一个具体的固定类型方法,这与 .net 中使用的在运行时解析类型的普通具体化泛型不同。
由于您无法内联接口,请尝试在接口上添加类型约束,这将捕获类型并避免泛化问题。
type IJsonSerializer<'T> =
abstract member SerializeToString : 'T -> string
...
我运行跨过这个的时候遇到了同样的问题。我刚刚写了这个,它似乎正在工作。我会 运行 进一步研究它,如果它很好,我会推出一个 NuGet 包,可以插入它。
open Giraffe.Serialization
open Microsoft.FSharpLu.Json
open Newtonsoft.Json
open System.IO
open System.Text
open System.Threading.Tasks
type FSharpLuJsonSerializer() =
interface IJsonSerializer with
member __.SerializeToString<'T> (x : 'T) =
Compact.serialize x
member __.SerializeToBytes<'T> (x : 'T) =
Text.Encoding.UTF8.GetBytes (Compact.serialize x)
member __.SerializeToStreamAsync<'T> (x : 'T) (stream : Stream) =
let serializer = new JsonSerializer()
serializer.Converters.Add (CompactUnionJsonConverter ())
use sw = new StreamWriter (stream)
use writer = new JsonTextWriter (sw)
serializer.Serialize (writer, obj)
Task.CompletedTask
member __.Deserialize<'T> (json : string) =
(Compact.deserialize >> box) json :?> 'T
member __.Deserialize<'T> (bytes : byte[]) =
(Compact.deserialize >> box) (Encoding.UTF8.GetString (ReadOnlySpan bytes)) :?> 'T
member __.DeserializeAsync<'T> (stream : Stream) =
Task.FromResult<'T> ((Compact.deserializeStream >> box) stream :?> 'T)
也欢迎来自 F# 人员的反馈。
跳转到 F# Slack 后,有一种更简单的方法可以做到这一点,不需要所有的装箱和转换。 FSharpLu 在底层使用 Newtonsoft.Json,其紧凑的功能来自 CompactUnionJsonConverter
类型。您可以将此转换器添加到 Giraffe 的默认 JsonSerializerSettings
,它可以工作 - 这意味着您不必完全重新实现 IJsonSerializer
.
open Giraffe.Serialization
open Microsoft.FSharpLu.Json
open Newtonsoft.Json
let jsonSettings =
let x = NewtonsoftJsonSerializer.DefaultSettings
x.Converters.Add (CompactUnionJsonConverter (true))
x
然后,您在哪里配置服务...
services.AddSingleton<IJsonSerializer>(NewtonsoftJsonSerializer jsonSettings)
非常感谢 Nino Floris (@ninofloris) 的建议。
我正在尝试在一个项目中从 Giraffe.Serialization.Json 实现 IJsonSerializer 以使用 Microsoft.FSharpLu.Json,但是我在使用其中一种通用方法(下面的代码)时遇到了问题
type FSharpLuSerializer () =
interface Giraffe.Serialization.Json.IJsonSerializer with
member __.Deserialize<'T> (json : string) =
Microsoft.FSharpLu.Json.Default.deserialize<'T> json
我遇到错误
This code is not sufficiently generic. The type variable ^T could not be generalized because it would escape its scope
我看到其他问题也有同样的错误,但我不确定如何将他们的解决方案应用到我的情况中。我想这与使用 ^T 的 FSharpLu.Json 有关,但我不知道我的解决方法是什么
https://github.com/Microsoft/fsharplu/blob/master/FSharpLu.Json/Default.fs
这是 Giraffe IJSonSerializer 接口
[<AllowNullLiteral>]
type IJsonSerializer =
abstract member SerializeToString<'T> : 'T -> string
abstract member SerializeToBytes<'T> : 'T -> byte array
abstract member SerializeToStreamAsync<'T> : 'T -> Stream -> Task
abstract member Deserialize<'T> : string -> 'T
abstract member Deserialize<'T> : byte[] -> 'T
abstract member DeserializeAsync<'T> : Stream -> Task<'T>
这是一个混乱的问题,一般来说,接口需要通过方法上的泛型来捕获要应用的类型,更复杂的是,你的 FSharpLu.Json 库正在使用内联 SRTP,SRTP 内联模板方法对 ^ 约束类型的调用,因此在编译后的代码中,每次调用都有一个具体的固定类型方法,这与 .net 中使用的在运行时解析类型的普通具体化泛型不同。
由于您无法内联接口,请尝试在接口上添加类型约束,这将捕获类型并避免泛化问题。
type IJsonSerializer<'T> =
abstract member SerializeToString : 'T -> string
...
我运行跨过这个的时候遇到了同样的问题。我刚刚写了这个,它似乎正在工作。我会 运行 进一步研究它,如果它很好,我会推出一个 NuGet 包,可以插入它。
open Giraffe.Serialization
open Microsoft.FSharpLu.Json
open Newtonsoft.Json
open System.IO
open System.Text
open System.Threading.Tasks
type FSharpLuJsonSerializer() =
interface IJsonSerializer with
member __.SerializeToString<'T> (x : 'T) =
Compact.serialize x
member __.SerializeToBytes<'T> (x : 'T) =
Text.Encoding.UTF8.GetBytes (Compact.serialize x)
member __.SerializeToStreamAsync<'T> (x : 'T) (stream : Stream) =
let serializer = new JsonSerializer()
serializer.Converters.Add (CompactUnionJsonConverter ())
use sw = new StreamWriter (stream)
use writer = new JsonTextWriter (sw)
serializer.Serialize (writer, obj)
Task.CompletedTask
member __.Deserialize<'T> (json : string) =
(Compact.deserialize >> box) json :?> 'T
member __.Deserialize<'T> (bytes : byte[]) =
(Compact.deserialize >> box) (Encoding.UTF8.GetString (ReadOnlySpan bytes)) :?> 'T
member __.DeserializeAsync<'T> (stream : Stream) =
Task.FromResult<'T> ((Compact.deserializeStream >> box) stream :?> 'T)
也欢迎来自 F# 人员的反馈。
跳转到 F# Slack 后,有一种更简单的方法可以做到这一点,不需要所有的装箱和转换。 FSharpLu 在底层使用 Newtonsoft.Json,其紧凑的功能来自 CompactUnionJsonConverter
类型。您可以将此转换器添加到 Giraffe 的默认 JsonSerializerSettings
,它可以工作 - 这意味着您不必完全重新实现 IJsonSerializer
.
open Giraffe.Serialization
open Microsoft.FSharpLu.Json
open Newtonsoft.Json
let jsonSettings =
let x = NewtonsoftJsonSerializer.DefaultSettings
x.Converters.Add (CompactUnionJsonConverter (true))
x
然后,您在哪里配置服务...
services.AddSingleton<IJsonSerializer>(NewtonsoftJsonSerializer jsonSettings)
非常感谢 Nino Floris (@ninofloris) 的建议。