簡體   English   中英

在 F# 記錄上實現具有通用參數的接口

[英]Implementing an interface with a generic parameter on a F# record

我正在嘗試在F# 記錄上實現Microsoft.Extensions.Logging.ILogger (為簡潔起見,復制如下)

using System;

namespace Microsoft.Extensions.Logging
{
    public interface ILogger
    {
        void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func<TState, Exception?, string> formatter);
        bool IsEnabled(LogLevel logLevel);
        IDisposable BeginScope<TState>(TState state);
    }
}

這是記錄實現。

  type ILoggerRcd<'TState> = 
    {
      BeginScope : 'TState -> IDisposable
      IsEnabled : LogLevel -> bool
      Log : LogLevel * EventId * 'TState * exn * Func<'TState,exn,string> -> unit
    }
    interface ILogger with
          override this.BeginScope(state : 'TState): IDisposable = 
              this.BeginScope (state)
          override this.IsEnabled(logLevel: LogLevel): bool = 
              this.IsEnabled logLevel
          override this.Log(logLevel: LogLevel, eventId: EventId, state : 'TState, ``exception``: exn, formatter: Func<'TState,exn,string>): unit = 
              this.Log(logLevel, eventId, state, ``exception``, formatter)

但是,我在 BeginScope 上收到此錯誤: One or more of the explicit class or function type variables for this binding could not be generalized, because they were constrained to other types

日志上The generic member 'Log' has been used at a non-uniform instantiation prior to this program point. Consider reordering the members so this member occurs first. Alternatively, specify the full type of the member explicitly, including argument types, return type and any additional generic parameters and constraints. The generic member 'Log' has been used at a non-uniform instantiation prior to this program point. Consider reordering the members so this member occurs first. Alternatively, specify the full type of the member explicitly, including argument types, return type and any additional generic parameters and constraints.

我已經閱讀了一些關於 fsharp 編譯器本身的問題,但似乎沒有什么是我遇到的這種情況。 這是可以做到的嗎?

ILogger接口要求您可以記錄任何類型的對象,但您嘗試僅記錄類型為'TState

獲取BeginScope的簽名:

IDisposable BeginScope<TState>(TState state);

看到那里的<TState>位了嗎? 這是一個通用參數。 這個簽名意味着每次任何人調用這個方法時,他們都可以選擇一個類型TState用於該調用。

同樣:調用者選擇泛型類型,而不是實現者。

例如:

let l : ILogger = ...
l.BeginScope 42   // TState = int
l.BeginScope true // TState = bool

這意味着您的BeginScope實現必須能夠使用任何類型,而不僅僅是創建ILoggerRcd記錄的類型。

這里的問題是您試圖約束ILogger'TState以匹配ILoggerRcd'TState ,但這不是您的選擇。

要看到這一點,請注意ILogger.BeginScope的調用者可以在一次調用中傳遞一個int state,然后在對同一個ILogger實例的另一次調用中傳遞一個string state。 您的實現試圖阻止這種情況,因此會出現編譯器錯誤。

我能看到的唯一方法是在你的類型中使用方法而不是函數記錄。 我認為沒有任何方法可以使用香草 F# 記錄來做你想做的事。

暫無
暫無

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

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