簡體   English   中英

實現接口時F#“代碼不夠通用”“^ 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

我收到了錯誤

此代碼不夠通用。 類型變量^ T無法一般化,因為它會逃避其范圍

我已經看到了同樣錯誤的其他問題,但我不確定如何將他們的解決方案應用於我的情況。 我想它與使用^ 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>

當我遇到同樣的問題時,我碰到了這個。 我剛剛寫了這篇文章,它似乎正在發揮作用。 我將通過更多步驟來運行它,如果它很好,我會推出一個可以插入的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)提出這個建議。

這是一個混亂的,一般來說,接口需要捕獲通過泛型在方法上應用的類型,使事情復雜化,你的FSharpLu.Json庫使用內聯SRTP,SRTP內聯模板對^約束類型的方法調用調用所以在編譯的代碼中,每個調用都有一個concreate,fixed類型的方法,這與.net中用於在運行時解析類型的常規reified通用不同。

由於無法內聯接口,請嘗試在接口上添加類型約束,以捕獲類型並避免泛化問題。

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM