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

时间:2011-11-13 23:01:32

标签: interface f# sequences

此时我已经超出了我的范围。我正在尝试创建一个类似的界面 此

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<string>seq<int>seq<record type here>seq<union type here>

有人能告诉我我在这里缺少什么吗?

感谢。

2 个答案:

答案 0 :(得分:4)

如果您使用Reflection加载接口实现,那么使用它将非常困难。问题是你得到了obj类型的对象。您知道它为某些IFetchData<'T>实施了'T,但在静态情况下,您不知道哪个'T。这是一个问题,因为您无法将对象强制转换为任何更具体的类型 - 如果您尝试使用IFetchData<obj>,它将无效,因为您无法将IFetchData<int>转换为{类型。

我建议使用非通用接口,这是非常常见的.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!

答案 1 :(得分:3)

您需要执行类似

的操作
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())