F#通用约束,使一个通用类型可以从另一通用类型继承 [英] F# Generic constraint to have one generic type inherit from another

查看:64
本文介绍了F#通用约束,使一个通用类型可以从另一通用类型继承的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在C#中,当一个通用参数继承自另一个通用参数时,例如:

In C# it is straightforward to define a generic class when one generic parameter inherits from another, e.g.:

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

这用于强制"执行.类 TClass 来实现接口 TInterface .我想在F#中做同样的事情,令人惊讶的是它似乎不起作用.例如,以下代码:

This is used to "force" the class TClass to implement the interface TInterface. I want to do the same in F# and surprisingly it does not seem to work. For example, the following code:

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

产生 FS0663 -此类型参数的使用方式将其始终限制为当'I:not struct'时为I" .我想知道是否可以在F#中实现类似于上面C#的通用结构.

results in FS0663 - This type parameter has been used in a way that constrains it to always be ''I when 'I : not struct'. I wonder if the generic structure similar to C# above can be implemented in F#.

在这一点上,也许有人会问为什么我需要那个?这就是为什么.我想与 CoreWCF 之类的通用F#互操作性:

At this point one may wonder why do I need that? Here is why. I want to have a generic F# interop with CoreWCFlike:

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

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

which then can be used as follows:

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 当然实现接口 IEchoWcfService .在没有通用约束的情况下, Startup 的问题是'S 实现上述'I 的问题是,没有什么可以阻止编写如下代码: .UseStartup<启动< EchoWcfService,字符串>>(),这当然会在运行时崩溃.

where EchoWcfService, of course, implement interface IEchoWcfService. The problem with Startup without generic constraint that 'S implements 'I as above is that nothing precludes writing something like: .UseStartup<Startup<EchoWcfService, string>>(), which will, of course, blow up at runtime.

更新

给出评论和参考,我试图以混合F#+ C#的方式解决该问题.因此,我们可以轻松地在C#中创建一个通用类:

Given the comments and the references I tried to address the issue in a mixed F# + C# way. So, we can easily create a generic class in 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>>>>()该类的项目.

and then change .UseStartup<Startup<EchoWcfService, IEchoWcfService>>() into .UseStartup<WcfStartup<EchoWcfService, IEchoWcfService>>() after referencing C# project with that class.

现在,如果我注释 where TService:class 而取消注释 where TService:class,TInterface ,则F#项目将不再使用编译.具有类型"EchoWcfService",但此处具有类型"IEchoWcfService" ,该类型与原始"类型基本相同.F#编译错误,但具有不同的风格.

Now, if I comment where TService : class and uncomment where TService : class, TInterface then F# project will no longer compile with This expression was expected to have type 'EchoWcfService' but here has type 'IEchoWcfService', which is basically the same as the "original" F# compile error but just with a different flavor.

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

推荐答案

根据F#规范:

格式类型的新约束:>'b再次被解析为type ='b.

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

There are some popular F# language suggestions that aim to solve this, see:

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

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

这篇关于F#通用约束,使一个通用类型可以从另一通用类型继承的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆