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#, и я также удалил дополнительные скобки, но в остальном ничего не изменилось....

Другие вопросы по тегам