У меня есть класс FileReader
, работа которого заключается в чтении и обработке текстовых файлов с использованием StreamReader
. Чтобы упростить модульное тестирование, я хотел бы предоставить параметр типа этому классу, чтобы я мог поменять StreamReader
на FakeReader
, который фактически не взаимодействует с файловой системой (и, возможно, выдает исключения, такие как OutOfMemory
, поэтому я могу проверить обработку ошибок в FileReader
).
В идеале я бы хотел определить FileReader
примерно так (тривиализировано для ясности):
type FileReader<'Reader> =
member this.Read file =
use sr = new 'Reader(file)
while not sr.EndOfStream do
printfn "%s" <| sr.ReadLine()
и просто определите FakeReader
, чтобы иметь конструктор, который принимает имя файла, метод получения свойства EndOfStream
, метод ReadLine()
и (пустой) метод Dispose()
. Однако F # имеет несколько претензий к этому определению типа, в том числе "Calls to object constructors on type parameters cannot be given arguments."
Поскольку StreamReader
не имеет конструктора по умолчанию, этот подход выглядит как запрет.
Пока что единственный способ, с помощью которого я получил это, - это наследовать FakeReader
от StreamReader
:
type FakeReader() =
inherit StreamReader("") with
override this.ReadLine() = "go away"
member this.EndOfStream = false
member this.Dispose() = ()
и используйте фабричный метод, который возвращает либо новый FakeReader
, либо новый 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()
Это выглядит намного менее элегантно. Есть ли способ сделать это с параметром типа? Спасибо всем.