Estoy tratando de implementar un Microsoft.Extensions.Logging.ILogger (copiado a continuación por brevedad) en un registro F #
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); } }Esta es la implementación del registro.
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) Sin embargo, recibo este error en 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 .
Y este error en Registro: 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.
He leído algunos problemas en el compilador fsharp, pero nada parece ser exactamente este caso. ¿Es esto algo que se puede hacer?
La interfaz ILogger requiere que pueda registrar objetos de cualquier tipo, pero está tratando de registrar solo aquellos del tipo 'TState .
Tome la firma de BeginScope :
IDisposable BeginScope<TState>(TState state); ¿Ves ese bit <TState> allí? Ese es un parámetro genérico. Esta firma significa que cada vez que alguien llama a este método, puede elegir un tipo de TState para usar con esa llamada.
Nuevamente: la persona que llama elige el tipo genérico, no el implementador.
Por ejemplo:
let l : ILogger = ... l.BeginScope 42 // TState = int l.BeginScope true // TState = bool Esto significa que su implementación de BeginScope debe poder funcionar con cualquier tipo, no solo con el tipo con el que se creó su registro ILoggerRcd .
El problema aquí es que está tratando de restringir el 'TState ILogger ILogger para que coincida con el ' ILoggerRcd 'TState , pero no es su elección hacerlo.
Para ver esto, tenga en cuenta que una persona que llama a ILogger.BeginScope puede pasar un estado int en una llamada y luego un estado de string en otra llamada a la misma instancia de ILogger . Su implementación intenta evitar esto, de ahí los errores del compilador.
La única forma en que puedo ver esto es usar métodos genéricos en su tipo en lugar de un registro de funciones. No creo que haya ninguna forma de hacer lo que quieras usando registros F # de vainilla.