简体   繁体   English

实现接口时F#“代码不够通用”“^ T无法推广”

[英]F# “Code is not sufficiently generic” “^T could not be generalized” when implementing interface

I'm trying to implement the IJsonSerializer from Giraffe.Serialization.Json in a project to use Microsoft.FSharpLu.Json, but am having trouble with one of the generic methods (code below) 我正在尝试从项目中的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

I am given the error 我收到了错误

This code is not sufficiently generic. 此代码不够通用。 The type variable ^T could not be generalized because it would escape its scope 类型变量^ T无法一般化,因为它会逃避其范围

I've seen other questions with the same error, but I'm not sure how to apply their solutions to my situation. 我已经看到了同样错误的其他问题,但我不确定如何将他们的解决方案应用于我的情况。 I imagine it has something to do with FSharpLu.Json using ^T, but I don't know what my workaround would be 我想它与使用^ T的FSharpLu.Json有关,但我不知道我的解决方法是什么

https://github.com/Microsoft/fsharplu/blob/master/FSharpLu.Json/Default.fs https://github.com/Microsoft/fsharplu/blob/master/FSharpLu.Json/Default.fs

Here's the Giraffe IJSonSerializer interface 这是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>

I ran across this when I encountered the same problem. 当我遇到同样的问题时,我碰到了这个。 I just wrote this, and it appears to be working. 我刚刚写了这篇文章,它似乎正在发挥作用。 I'll run it through some more paces, and if it's good, I'll push out a NuGet package where this can be plugged in. 我将通过更多步骤来运行它,如果它很好,我会推出一个可以插入的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)

Feedback from F# folks welcomed as well. 来自F#人们的反馈也受到欢迎。

After jumping onto the F# Slack, there is an even easier way to do this that doesn't require all the boxing and casting. 跳到F#Slack后,有一种更简单的方法可以做到这一点,不需要所有的拳击和施法。 FSharpLu uses Newtonsoft.Json under the hood, and its compact functionality comes from the CompactUnionJsonConverter type. FSharpLu使用了Newtonsoft.Json,其紧凑的功能来自CompactUnionJsonConverter类型。 You can add this converter to Giraffe's default JsonSerializerSettings , and it works - meaning that you don't have to completely reimplement IJsonSerializer . 您可以将此转换器添加到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

Then, where you configure your services... 然后,在哪里配置您的服务......

    services.AddSingleton<IJsonSerializer>(NewtonsoftJsonSerializer jsonSettings)

Huge thanks to Nino Floris (@ninofloris) for this suggestion. 非常感谢Nino Floris(@ninofloris)提出这个建议。

this is a messy one, in general, interfaces need to capture the type to be applied via generics on methods, to complicate matters further, your FSharpLu.Json library is using inline SRTPs, SRTPs inline template the method invocation call on the ^ constraint type so in the compiled code there is a concreate, fixed type method for each call, this is different to the normal reified generics used in .net that resolve the type at runtime. 这是一个混乱的,一般来说,接口需要捕获通过泛型在方法上应用的类型,使事情复杂化,你的FSharpLu.Json库使用内联SRTP,SRTP内联模板对^约束类型的方法调用调用所以在编译的代码中,每个调用都有一个concreate,fixed类型的方法,这与.net中用于在运行时解析类型的常规reified通用不同。

As you cannot inline an interface, try add the type constraint on the interface, that will catch the type and avoid the generalisation issue. 由于无法内联接口,请尝试在接口上添加类型约束,以捕获类型并避免泛化问题。

type IJsonSerializer<'T> =
abstract member SerializeToString      : 'T -> string
...

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM