在非泛型 F# 接口中创建和使用泛型函数

发布于 2024-12-15 01:18:35 字数 720 浏览 3 评论 0原文

目前这超出了我的范围。我正在尝试创建一个看起来像这样的界面 这。

type IFetchData = 
     abstract FetchData: string -> seq<'a>

上面的声明是有效的(并且可以编译),但是当我使用它时,我收到编译时错误。该表达式的预期类型为“a”,但这里的类型为“我当前尝试返回的内容”,即 seq。

然而,我的示例用法如下所示:

type SampleFetchData() =
    interface IFetchData with
        member self.FetchData str =           
           seq {
                    for letter in str do
                        yield letter // compile error here
                }

我不确定我做错了什么。我想做的就是允许接口实现者能够编写返回通用序列的任何函数 seq,seq,< code>seq<此处的记录类型>、seq<此处的联合类型> 等。

有人可以告诉我这里缺少什么吗?

谢谢。

It is beyond me at this point. I'm trying to create an interface that looks something like
this.

type IFetchData = 
     abstract FetchData: string -> seq<'a>

The above declaration is valid (and compiles) but when I go to use it I get a compile time error. This expression was expected to have type 'a but here has type "what I'm currently trying to return" i.e. seq.

My example usage however looks like the following:

type SampleFetchData() =
    interface IFetchData with
        member self.FetchData str =           
           seq {
                    for letter in str do
                        yield letter // compile error here
                }

I'm not sure what I'm doing wrong. All I'd like to do is allow the interface implementer to be able to write any function that returns a generic sequence either seq<string>,seq<int>,seq<record type here>, seq<union type here>, etc.

Can someone tell me what I'm missing here?

Thanks.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

只是一片海 2024-12-22 01:18:35

如果您使用反射加载接口实现,那么使用它将会非常困难。问题是你得到了一个 obj 类型的对象。您知道它为某些 'T 实现了 IFetchData<'T>,但静态地,您不知道为哪个 'T。这是一个问题,因为您无法将对象强制转换为任何更具体的类型 - 如果您尝试使用 IFetchData,它将不起作用,因为您无法强制转换,例如 < code>IFetchData为该类型。

我建议使用非通用接口,这是非常常见的 .NET 模式:

type IFetchDataUntyped = 
  abstract FetchData : string -> System.Collections.IEnumerable

type IFetchData<'T> =  
  inherit IFetchDataUntyped
  abstract FetchData : string -> seq<'T> 

当您使用 Reflection 加载实现时,您可以将对象强制转换为 IFetchDataUntyped 并以相当合理的方式使用它(使用 Seq.cast 将序列转换为更具体的类型(如果您知道元素类型)。

根据您的应用程序,您还可以将 FetchData 方法设置为通用方法,并保持接口非通用。然后,您可以将动态加载的对象强制转换为接口并调用该方法。然而,这改变了设计(因为该方法必须适用于它作为类型参数获取的任何类型):

type IFetchData =  
  abstract FetchData<'T> : string -> seq<'T>  // Note: Generic parameter here!

If you're loading the interface implementation using Reflection, then it is going to be quite difficult to work with it. The problem is that you get an object of type obj. You know that it implements IFetchData<'T> for some 'T, but statically, you don't know for which 'T. This is a problem because you can't cast the object to any more specific type - if you tried using IFetchData<obj>, it wouldn't work because you can't cast, for example, IFetchData<int> to that type.

I would recommend using a non-generic interface, which is quite common .NET pattern:

type IFetchDataUntyped = 
  abstract FetchData : string -> System.Collections.IEnumerable

type IFetchData<'T> =  
  inherit IFetchDataUntyped
  abstract FetchData : string -> seq<'T> 

When you load an implementation using Reflection, you can cast the object to IFetchDataUntyped and work with it in a fairly reasonable way (using Seq.cast to convert the sequence to a more specific type if you know the element type).

Depending on your application, you may also just make the FetchData method a generic method and keep the interface non-generic. Then you could cast dynamically loaded objects to the interface and invoke the method. However, this changes the design (because the method has to work for any type it gets as a type parameter):

type IFetchData =  
  abstract FetchData<'T> : string -> seq<'T>  // Note: Generic parameter here!
芯好空 2024-12-22 01:18:35

您需要做类似的事情

type IFetchData<'a> = 
     abstract FetchData: string -> seq<'a>

type SampleFetchData() =
    interface IFetchData<char> with
        member self.FetchData str =           
           seq {
                    for letter in str do
                        yield letter 
                }

,即接口需要变得通用。如果您想避免通用性,您可以使用一些内联约束,而不是接口

编辑:内联魔术版本

let inline Fetchdata string obj=
   (^a: (member FetchData: string -> seq<'b> )(obj, string))

type SampleFetchData() =
        member self.FetchData str =           
           seq {
                    for letter in str do
                        yield letter 
                }

Fetchdata "hello" (new SampleFetchData())

You need to do something like

type IFetchData<'a> = 
     abstract FetchData: string -> seq<'a>

type SampleFetchData() =
    interface IFetchData<char> with
        member self.FetchData str =           
           seq {
                    for letter in str do
                        yield letter 
                }

i.e. the interface needs to be made generic. If you want to avoid the genericness you could use some inline constraints, rather than an interface

EDIT: Inline magic version

let inline Fetchdata string obj=
   (^a: (member FetchData: string -> seq<'b> )(obj, string))

type SampleFetchData() =
        member self.FetchData str =           
           seq {
                    for letter in str do
                        yield letter 
                }

Fetchdata "hello" (new SampleFetchData())
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文