generics - F# 泛型约束使一个泛型类型从另一个继承

标签 generics f# type-constraints

在 C# 中,当一个泛型参数继承自另一个泛型参数时,可以直接定义一个泛型类,例如:

public class MyClass<TClass, TInterface> where TClass : class, TInterface
{
}
    

这用于“强制”类 TClass实现接口(interface) TInterface .我想在 F# 中做同样的事情,但令人惊讶的是它似乎不起作用。例如下面的代码:

type Startup<'S, 'I when 'I : not struct and 'S : not struct and 'S :> 'I>() =
    member _.x = 0

结果为 FS0663 - This type parameter has been used in a way that constrains it to always be ''I when 'I : not struct' .想知道上面类似C#的泛型结构是否可以在F#中实现。

此时有人可能想知道我为什么需要它?这就是为什么。我想要一个通用的 F# 互操作 CoreWCF喜欢:

open CoreWCF
open CoreWCF.Configuration
open Microsoft.AspNetCore.Builder
open Microsoft.AspNetCore.Hosting
open Microsoft.Extensions.DependencyInjection

module Startup =

    type Startup<'S, 'I when 'I : not struct and 'S : not struct>() =
        let createServiceModel (builder : IServiceBuilder) = 
            builder
                .AddService<'S>()
                .AddServiceEndpoint<'S, 'I>(new BasicHttpBinding(), "/basichttp")
                .AddServiceEndpoint<'S, 'I>(new NetTcpBinding(), "/nettcp")
            |> ignore

        member _.ConfigureServices(services : IServiceCollection) =
            do services.AddServiceModelServices() |> ignore

        member _.Configure(app : IApplicationBuilder, env : IHostingEnvironment) =
            do app.UseServiceModel(fun builder -> createServiceModel builder) |> ignore

然后可以按如下方式使用:

open System.Net
open CoreWCF.Configuration
open Microsoft.AspNetCore
open Microsoft.AspNetCore.Hosting
open Microsoft.AspNetCore.Server.Kestrel.Core

module Builder =
    let CreateWebHostBuilder() : IWebHostBuilder =
        let applyOptions (options : KestrelServerOptions) =
            let address : IPAddress = IPAddress.Parse("192.168.1.89")
            let port = 8080
            let endPoint : IPEndPoint = new IPEndPoint(address, port)
            options.Listen(endPoint)

        WebHost
            .CreateDefaultBuilder()
            .UseKestrel(fun options -> applyOptions options)
            .UseNetTcp(8808)
            .UseStartup<Startup<EchoWcfService, IEchoWcfService>>()

哪里EchoWcfService ,当然,实现接口(interface)IEchoWcfService . Startup 的问题没有通用约束 'S工具 'I如上所述,没有什么可以阻止写这样的东西:.UseStartup<Startup<EchoWcfService, string>>() ,当然会在运行时爆炸。

更新

鉴于评论和引用资料,我尝试以 F# + C# 的混合方式解决该问题。因此,我们可以轻松地在 C# 中创建泛型类:

public class WcfStartup<TService, TInterface>
    where TService : class
    //where TService : class, TInterface
{
    private void CreateServiceModel(IServiceBuilder builder)
    {
        builder
            .AddService<TService>()
            .AddServiceEndpoint<TService, TInterface>(new BasicHttpBinding(), "/basichttp")
            .AddServiceEndpoint<TService, TInterface>(new NetTcpBinding(), "/nettcp");
    }

    public void ConfigureServices(IServiceCollection services) =>
        services.AddServiceModelServices();

    public void Configure(IApplicationBuilder app, IHostingEnvironment env) =>
        app.UseServiceModel(CreateServiceModel);
}

然后更改.UseStartup<Startup<EchoWcfService, IEchoWcfService>>()进入.UseStartup<WcfStartup<EchoWcfService, IEchoWcfService>>()在使用该类引用 C# 项目之后。

现在,如果我评论 where TService : class并取消注释 where TService : class, TInterface那么 F# 项目将不再使用 This expression was expected to have type 'EchoWcfService' but here has type 'IEchoWcfService' 进行编译,这与“原始”F# 编译错误基本相同,只是风格不同。

我将其称为 F# 编译器中的错误...

最佳答案

来自 F# 规范:

形式 type :> 'b 的新约束再次解决为 type = 'b。

有一些流行的 F# 语言建议旨在解决此问题,请参阅:

https://github.com/fsharp/fslang-suggestions/issues/255

https://github.com/fsharp/fslang-suggestions/issues/162

关于generics - F# 泛型约束使一个泛型类型从另一个继承,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/64103022/

相关文章:

java - 在运行时访问参数化类型信息

.net - 异常处理中的选项与异常

typescript - 如何在 TypeScript 中创建一个适用于数字和字符串的通用加法运算符

c# - 动态创建T时typeof(T)为null?

java - 泛型通配符 : Assigning Super class object to a Subclass object list

javascript - 可以使用 websharper 作为 JS 的替代品吗?

asynchronous - 如何在 FsCheck 中运行异步测试?

scala - 通过将类型参数与参数的路径相关类型进行匹配来约束操作

scala - 如何从 TypeClass 解析中排除特定类型?

java - 泛型和继承 - 代码中只需要父类(super class)型