F#相当于一个公共C#泛型方法

时间:2013-06-23 21:13:58

标签: generics f# c#-to-f#

我正在尝试在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是来自带有[FlagsAttribute]的C#程序集的枚举。 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 个答案:

答案 0 :(得分:7)

代码的另一部分肯定有问题。以下最小样本具有完全相同的MyMethod定义,并且正常工作(粘贴到新脚本文件中时):

type Thing<'T> = T of 'T

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

我删除了public修饰符,因为这是F#中成员的默认设置,我也删除了其他括号,但是没有任何更改......