F# F:没有默认构造函数作为类型参数的类?

F# F:没有默认构造函数作为类型参数的类?,f#,F#,我有一个FileReader类,其工作是使用StreamReader读取和处理文本文件。为了便于单元测试,我想为这个类提供一个类型参数,这样我就可以将StreamReader替换为一个实际上不与文件系统交互的FakerReader,并且可能抛出异常,例如OutOfMemory,这样我就可以在FileReader中测试错误处理 理想情况下,为了清晰起见,我想对FileReader进行如下定义: type FileReader<'Reader> = member this.Rea

我有一个FileReader类,其工作是使用StreamReader读取和处理文本文件。为了便于单元测试,我想为这个类提供一个类型参数,这样我就可以将StreamReader替换为一个实际上不与文件系统交互的FakerReader,并且可能抛出异常,例如OutOfMemory,这样我就可以在FileReader中测试错误处理

理想情况下,为了清晰起见,我想对FileReader进行如下定义:

type FileReader<'Reader> =
    member this.Read file =
        use sr = new 'Reader(file)
        while not sr.EndOfStream do
            printfn "%s" <| sr.ReadLine()
并使用工厂方法,根据需要返回新的FakerReader或新的StreamReader

type ReaderType = Fake | SR
let readerFactory (file : string, readerType) =
    match readerType with
    | Fake -> new FakeReader() :> StreamReader
    | SR -> new StreamReader(file)

type FileReader(readertype) =
    member this.Read file =
        use sr = readerFactory(file, readertype)
        while not sr.EndOfStream do
            printfn "%s" <| sr.ReadLine()

这看起来不那么优雅。有没有一种方法可以通过类型参数实现这一点?多亏了all。

您可以传入一个函数,该函数构造并返回所需类型的对象

type FileReader(f : string -> TextReader) =
    member this.Read file =
        use sr = f file
        while sr.Peek() <> -1 do
            printfn "%s" <| sr.ReadLine()

type FakeReader() =
    inherit StringReader("")
    override this.ReadLine() = "go away"
    override this.Peek() = 0

let reader1 = new FileReader(fun fn -> new StreamReader(fn) :> _)

let reader2 = new FileReader(fun fn -> new FakeReader() :> _)

强制转换是必要的,因为我删除了泛型类型参数,但可以推断实际类型。

您可以传入一个函数,该函数构造并返回所需类型的对象

type FileReader(f : string -> TextReader) =
    member this.Read file =
        use sr = f file
        while sr.Peek() <> -1 do
            printfn "%s" <| sr.ReadLine()

type FakeReader() =
    inherit StringReader("")
    override this.ReadLine() = "go away"
    override this.Peek() = 0

let reader1 = new FileReader(fun fn -> new StreamReader(fn) :> _)

let reader2 = new FileReader(fun fn -> new FakeReader() :> _)

强制转换是必要的,因为我删除了泛型类型参数,但是可以推断出实际的类型。

使用MizardX建议的创建reader对象的函数是您问题的直接答案。不过,我可能会考虑使用与TeXReDead不同的抽象。正如Ankur在评论中提到的,您可以使用更实用的方法

如果您只是使用TextReader从输入中读取文本行,则可以使用seq类型。FileReader类型实际上可能只是一个接受seq的函数,尽管这可能过于简单化了。。。视情况而定

这使它更具功能性—在函数式编程中,您经常使用函数转换数据结构,这正是本示例所做的:

open System.IO

/// Creates a reader that reads data from a file
let readFile (file:string) = seq {
  use rdr = new StreamReader(file)
  let line = ref ""
  while (line := rdr.ReadLine(); !line <> null) do
    yield !line }

/// Your function that processes the input (provided as a sequence)
let processInput input = 
  for s in input do 
    printfn "%s" s

readFile "input.txt" |> processInput

使用MizardX建议的创建reader对象的函数可以直接回答您的问题。不过,我可能会考虑使用与TeXReDead不同的抽象。正如Ankur在评论中提到的,您可以使用更实用的方法

如果您只是使用TextReader从输入中读取文本行,则可以使用seq类型。FileReader类型实际上可能只是一个接受seq的函数,尽管这可能过于简单化了。。。视情况而定

这使它更具功能性—在函数式编程中,您经常使用函数转换数据结构,这正是本示例所做的:

open System.IO

/// Creates a reader that reads data from a file
let readFile (file:string) = seq {
  use rdr = new StreamReader(file)
  let line = ref ""
  while (line := rdr.ReadLine(); !line <> null) do
    yield !line }

/// Your function that processes the input (provided as a sequence)
let processInput input = 
  for s in input do 
    printfn "%s" s

readFile "input.txt" |> processInput

你真的希望它是基于面向对象的,还是其他更实用的方法也适用于你?一个更实用的方法实际上会很好。我倾向于依靠面向对象的方法,因为我在命令行工作了很多年。你真的希望它是基于面向对象的,还是其他更实用的方法也适用于你?一个更实用的方法实际上会很棒。我倾向于依靠OOP拐杖,因为我在命令行工作了多年。我认为函数可以是string->TextReader,您不需要类型参数,因为FileReader中的实现只使用TextReader的公共方法,而不使用实际读取器的类型。啊,请稍候,尝试修复格式设置。谢谢你们的回复-如果我理解正确,你在想:type ReaderType=false | SR让readerFactory ReaderType file:string=将ReaderType与| false->new FakeReader:>StreamReader | SR->new StreamReaderfile type FileReaderfactory:string->StreamReader=成员this.Read file=使用SR=工厂文件而不是sr.EndOfStream不打印fn%s关闭;你可以跳过额外的类型:new FileReaderfun fn->new StreamReaderfn vs new FileReaderfun fn->new fakereader谢谢你,很抱歉格式弄乱了,因为我在弄清楚如何修复它之前已经没有时间编辑了。我想这个函数可以是string->TextReader,你不需要类型参数,因为FileReader中的实现只使用TextReader的公共方法,而不使用实际reader的类型。Argh,准备,尝试修复格式。谢谢你们的回复-如果我理解正确,你在想:type ReaderType=false | SR让readerFactory ReaderType file:string=将ReaderType与| false->new FakeReader:>StreamReader | SR->new StreamReaderfile type FileReaderfactory:string->StreamReader=成员this.Read file=使用SR=工厂文件而不是sr.EndOfStream不打印fn%s关闭;您可以跳过额外的类型:new FileReaderfun fn->new StreamReaderfn vs new FileReaderfun fn->new fakereader感谢您,并对格式混乱表示抱歉,因为我没有时间去编辑,还没有弄清楚如何修复它。这确实比重新实现TextReader更干净,而且很高兴知道我没有遗漏任何明显的东西,这些东西会使我的类型参数
第三种方法的工作。一如既往地感谢Tomas。这确实比重新实现TextReader更干净,而且很高兴知道我没有遗漏任何明显的东西,这些东西会使我的类型参数方法起作用。一如既往地谢谢你,托马斯。