2013-06-23 2 views
1

Я пытаюсь создать открытый метод в классе в 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 *) 
    () 

Этот код генерирует ошибку компиляции:

error FS0670: This code is not sufficiently generic. The type variable 'T could not be generalized because it would escape its scope.

Если я стараюсь следующее, я могу компиляция:

member public this.MyMethod((name : System.String), (thingToProcess : Thing<_>)) = 
    (* Some code *) 
    () 

Но, tryi нг вызвать метод из C#, как не удается следующее:

public void DoSomething<T>(Thing<T> thingToProcess) 
{ 
    _instanceOfFSharpClass.MyMethod("A string", thingToProcess); 
} 

с ошибкой компилятора:

The best overloaded method match for 'MyFSharpClass.MyMethod(string, Thing)' has some invalid arguments.

предложения? Как создать этот тип метода в F #? Если этот метод не может быть создан в F #, что является разумным обходным решением? Мне нужно избегать отливки Thing<T> до Thing<object>, если это возможно.

Edit:

Здесь больше 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 (или эквивалент) может иметь другой параметр типа.

+0

Я сделаю, если позволит время, отредактировать вопрос для краткости и ясности, если есть четкое решение. Ошибка компилятора немного мистифицирует новичку F #, а подход Google-the-error-message не дал никаких результатов, которые, казалось бы, применимы. Я надеюсь, что другой программист, столкнувшись с сообщением об ошибке, может извлечь выгоду из этого вопроса и ответа. – Andrew

ответ

7

Должно быть что-то не так с другой частью вашего кода. Следующий минимальный образец имеет точно такое же определение MyMethod и работает нормально (при вставке в новый файл сценария):

type Thing<'T> = T of 'T 

type Foo() = 
    member this.MyMethod<'T>(name:string, thingToProcess:Thing<'T>) = 
    () 

Я удалил модификатор public, потому что по умолчанию для членов в F #, и я также удалены дополнительные скобки, но в остальном ничего не изменилось ....

+0

Очень вероятно. Я добавлю несколько подробностей и посмотрю, поможет ли это. – Andrew

+0

Да, если вы можете дать минимальный образец, который дает ошибку, это позволило бы дать более полезный ответ! –

+0

Спасибо. Я добавил код, как бы он ни был нелестен. Я довольно новичок в F #, как вы могли догадаться. – Andrew