#interface #f# #sequences
#интерфейс #f# #последовательности
Вопрос:
На данный момент это выше моего понимания. Я пытаюсь создать интерфейс, который выглядит примерно так.
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>
, и т.д.
Может кто-нибудь сказать мне, чего мне здесь не хватает?
Спасибо.
Ответ №1:
Если вы загружаете реализацию интерфейса с использованием отражения, то работать с ней будет довольно сложно. Проблема в том, что вы получаете объект типа obj
. Вы знаете, что она реализуется IFetchData<'T>
для некоторых 'T
функций, но статически вы не знаете, для каких 'T
. Это проблема, потому что вы не можете привести объект к какому-либо более определенному типу — если бы вы попытались использовать IFetchData<obj>
, это не сработало бы, потому что вы не можете привести, например, IFetchData<int>
к этому типу.
Я бы рекомендовал использовать неродовой интерфейс, который является довольно распространенным .СЕТЕВОЙ шаблон:
type IFetchDataUntyped =
abstract FetchData : string -> System.Collections.IEnumerable
type IFetchData<'T> =
inherit IFetchDataUntyped
abstract FetchData : string -> seq<'T>
Когда вы загружаете реализацию с использованием отражения, вы можете привести объект к IFetchDataUntyped
и работать с ним довольно разумным способом (используя Seq.cast
для преобразования последовательности в более конкретный тип, если вы знаете тип элемента).
В зависимости от вашего приложения вы также можете просто сделать FetchData
метод универсальным методом и сохранить интерфейс нестандартным. Затем вы могли бы передавать динамически загружаемые объекты в интерфейс и вызывать метод. Однако это меняет дизайн (поскольку метод должен работать для любого типа, который он получает в качестве параметра типа):
type IFetchData =
abstract FetchData<'T> : string -> seq<'T> // Note: Generic parameter here!
Комментарии:
1. Спасибо, Томас! Мне придется попробовать. В конечном счете, чего я пытаюсь достичь, это загрузить сборку, создать экземпляр типа, который в конечном итоге будет obj, а затем привести к интерфейсу для вызова метода. Затем я хочу взять результат (некоторый seq<‘T>) и сохранить для последующего использования по потоку. В конечном итоге она будет передана другой функции для обработки… задача целевых функций — узнать тип seq. Еще раз спасибо!
Ответ №2:
Вам нужно сделать что-то вроде
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
}
т.е. интерфейс должен быть сделан универсальным. Если вы хотите избежать универсальности, вы могли бы использовать некоторые встроенные ограничения, а не интерфейс
РЕДАКТИРОВАТЬ: встроенная версия magic
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())
Комментарии:
1. Спасибо за ответ, Джон. Я этого боялся. Я заставил ее работать, используя ваш ответ выше, но в конечном итоге это можно использовать с помощью отражения. И вот тут-то и возникает следующая проблема. По какой-то причине у меня возникают проблемы с динамической загрузкой и вызовом указанного выше элемента из-за универсальности интерфейса. Я надеялся, что мне не придется делать интерфейс универсальным, но у меня все еще будет универсальная функция. 🙂
2. Я обновил встроенную версию, которая может быть лучше, в зависимости от того, как работает ваше отражение.
3. Вау! Очень круто! На данный момент я придерживаюсь очень старой школы. Я в основном сопоставляю интерфейсы данного типа, чтобы определить, реализует ли он интерфейс выше, и если это так, то я создаю его экземпляр и пытаюсь привести его к интерфейсу выше и, наконец, вызываю элемент выборки данных. В вашем примере выше мне не нужно было приводить, я мог бы просто вызвать встроенную функцию выше с отраженным типом и параметрами. Я думаю, это правильно? 🙂
4. @cameron — встроенную функцию можно вызвать для любого объекта, если тип известен во время компиляции