在 F# 中的 C# 派生 类 之间进行转换

casting between C# derived classes, in F#

我有两种来自 C# 库的类型:

一个是这样定义的(只有签名):

public class CallResult<T>: CallResult
{
    public CallResult([AllowNull]T data, Error? error): base(error)
    public static implicit operator bool(CallResult<T> obj)
    public bool GetResultOrError([MaybeNullWhen(false)] out T data, [NotNullWhen(false)] out Error? error)
    public new static WebCallResult<T> CreateErrorResult(Error error)
}

第二个派生自它:

public class WebCallResult<T>: CallResult<T>
{
    public HttpStatusCode? ResponseStatusCode { get; set; }
    public IEnumerable<KeyValuePair<string, IEnumerable<string>>>? ResponseHeaders { get; set; }
    public WebCallResult(HttpStatusCode? code, IEnumerable<KeyValuePair<string, IEnumerable<string>>>? responseHeaders, [AllowNull] T data, Error? error): base(data, error)
    public WebCallResult(WebCallResult<T> callResult): base(callResult.Data, callResult.Error)
    public static WebCallResult<T> CreateFrom<Y>(WebCallResult<Y> source) where Y : T
    public static WebCallResult<T> CreateErrorResult(HttpStatusCode? code, IEnumerable<KeyValuePair<string, IEnumerable<string>>>? responseHeaders, Error error)
}

他们都来自:

public class CallResult
{
    public Error? Error { get; internal set; }
    public bool Success => Error == null;
    public CallResult(Error? error)
    public static implicit operator bool(CallResult obj)
    public static WebCallResult CreateErrorResult(Error error)
}

一些 api 调用 return CallResult,其他 return WebCallResult。

现在我用两次相同的代码来处理它:

// turn a webcall result into a Result object
let processResultWeb (applyOnOk: 'a -> 'b) (result: WebCallResult<'a>) =
    match result.Success with
    | true ->  Result.Ok (applyOnOk result.Data)
    | false -> Result.Error (decodeError result.Error)

// turn a webcall result into a Result object
let processResult (applyOnOk: 'a -> 'b) (result: CallResult<'a>) =
    match result.Success with
    | true ->  Result.Ok (applyOnOk result.Data)
    | false -> Result.Error (decodeError result.Error)

这没有任何意义,因为它是相同的代码,而且我只关心来自基础 class (CallResult) 的数据。

所以我想将这两种类型都转换为基础 class:

let a: WebCallResult = ...
let r = a :> CallResult

但这会导致编译器错误:

[FS0001] The type 'CallResult' is not compatible with the type 'WebCallResult<'a>'

如何通过仅从它们的基础 class 访问字段来检查两种类型的结果,但使用相同的通用类型。

编辑: classes 的源代码在这里:https://pastebin.com/mrw5W7xk

问题是我想从:

WebCallResult<'a'> to CallResult<'a>

通用似乎是问题所在。

使用您的代码,即使使用泛型,我也可以毫无问题地进行转换。这是一个例子:

let foo x =
    WebCallResult<_>(System.Nullable(), Array.empty, x, Error())

let a : WebCallResult<int> = foo 3
let r = a :> CallResult<_>

let b : WebCallResult<string> = foo "str"
let q = b :> CallResult<_>