F# эквивалент общедоступного универсального метода C#
Я пытаюсь создать публичный метод в классе в F#. Эквивалент в C# будет:
public void MyMethod<T>(string name, Thing<T> thingToProcess)
{
// Do stuff
}
В F# я пытаюсь:
member public this.MyMethod<'T>((name : System.String), (thingToProcess : Thing<'T>)) =
(* Do similar stuff *)
()
Этот код генерирует ошибку компилятора:
Ошибка FS0670: этот код недостаточно универсален. Переменная типа 'T не может быть обобщена, потому что она выходит за пределы своей области видимости.
Если я попробую следующее, я смогу скомпилировать:
member public this.MyMethod((name : System.String), (thingToProcess : Thing<_>)) =
(* Some code *)
()
Но попытка вызвать метод из C#, как показано ниже, не удалась:
public void DoSomething<T>(Thing<T> thingToProcess)
{
_instanceOfFSharpClass.MyMethod("A string", thingToProcess);
}
с ошибкой компилятора:
Наилучшее совпадение перегруженного метода для 'MyFSharpClass.MyMethod(string, Thing)' имеет несколько недопустимых аргументов.
Предложения? Как мне создать этот тип метода в F#? Если этот метод не может быть создан в F#, какой разумный обходной путь? Мне нужно избегать кастинга Thing<T>
в Thing<object>
если вообще возможно.
Редактировать:
Вот еще код F#. Я постараюсь придерживаться наиболее подходящих частей. EnumWithFlags
перечисление из сборки C# с [FlagsAttribute]
, cacheOne
заполняется другими методами, не перечисленными здесь. IInterface
определяется в сборке C# и имеет только один метод, void ReceiveThing<T>(string name, Thing<T> thingToProcess)
, Функция TranslateThing
имеет подпись val TranslateThing : (Guid -> Thing<'T> -> TranslatedThing<'T>)
, Это помогает?
type TranslatedThing<'T> =
| FirstThing of Thing<'T>
| SecondThing of Thing<System.String>
| ThirdThing of Thing<byte[]>
| FourthThing of Thing<System.String>
| IgnoreThing
[<AbstractClass>]
type public MyAbstractClass() =
let cacheOne = new ConcurrentDictionary<EnumWithFlags, Dictionary<Guid, IInterface>>()
member public this.MyMethod<'T>((name : System.String), (thingToProcess : Thing<'T>)) =
cacheOne.Keys.Where(fun key -> match key with
| k when (k &&& thingToProcess.EnumWithFlagsProperty) = EnumWithFlags.None -> false
| _ -> true)
.SelectMany(fun key -> cacheOne.[key].AsEnumerable())
.Distinct(
{
new IEqualityComparer<KeyValuePair<Guid, IInterface>> with
member x.Equals(a, b) = a.Key = b.Key
member x.GetHashCode y = y.Key.GetHashCode()
})
.AsParallel()
.Select(new Func<KeyValuePair<Guid, IInterface>, Tuple<IInterface, TranslatedThing<_>>>(fun kvp -> new Tuple<IInterface, TranslatedThing<'T>>(kvp.Value, TranslateThing kvp.Key thingToProcess)))
.Where(new Func<Tuple<IInterface, TranslatedThing<'T>>, bool>(fun t -> t.Item2 <> IgnoreThing))
.ForAll(new Action<Tuple<IInterface, TranslatedThing<'T>>>(fun t ->
match t.Item2 with
| FirstThing(x) -> t.Item1.ReceiveThing(name, x)
| SecondThing(x) -> t.Item1.ReceiveThing(name, x)
| ThirdThing(x) -> t.Item1.ReceiveThing(name, x)
| FourthThing(x) -> t.Item1.ReceiveThing(name, x)
| _ -> ()))
Другое редактирование:
После долгих перегонок, я думаю, я примерно понимаю, что является причиной проблемы. Я остановился на последней строке MyMethod
потому что удаление не решило ошибку. Эта строка была:
cacheTwo.Remove(thingToProcess) |> ignore
где cacheTwo
определен ранее в классе:
let cacheTwo = new Dictionary<Thing<'T>, SpecificThingTranslator<'T>>
где подпись SpecificThingTranslator<'T>
является:
type SpecificThingTranslator<'T> =
{First: TranslatedThing<'T>;
Second: Lazy<TranslatedThing<'T>>;
Third: Lazy<TranslatedThing<'T>>;
Fourth: Lazy<TranslatedThing<'T>>;}
Устранение cacheTwo
линия не решила ошибку, потому что функция TranslateThing
относится к cacheTwo
, в конце концов. Исключая все ссылки на cacheTwo
устраняет ошибку.
Я, вероятно, могу найти обходной путь для карты Thing<'T>
в SpecificThingTranslator<'T>
, Тем не менее, я что-то здесь упустил? Я забыл о коллекции.NET (или, может быть, F#-специфичной), которая позволит это сопоставление? Хотя параметр типа для ключа и значения каждой пары должен быть одинаковым, каждый KeyValuePair (или эквивалентный) может иметь другой параметр типа.
1 ответ
Должно быть что-то не так с другой частью вашего кода. Следующий минимальный образец имеет точно такое же определение MyMethod
и работает нормально (при вставке в новый файл скрипта):
type Thing<'T> = T of 'T
type Foo() =
member this.MyMethod<'T>(name:string, thingToProcess:Thing<'T>) =
()
Я удалил public
модификатор, потому что это значение по умолчанию для членов в F#, и я также удалил дополнительные скобки, но в остальном ничего не изменилось....