【问题标题】:F# equivalent of a public C# generic methodF# 等效于公共 C# 泛型方法
【发布时间】:2013-06-23 21:13:58
【问题描述】:

我正在尝试在 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&lt;T&gt; 转换为Thing&lt;object&gt;

编辑:

这里有更多的 F# 代码。我会尽量坚持可能相关的部分。 EnumWithFlags 是来自带有 [FlagsAttribute] 的 C# 程序集的枚举。 cacheOne 以此处未列出的其他方法填充。 IInterface 在 C# 程序集中定义,只有一种方法,void ReceiveThing&lt;T&gt;(string name, Thing&lt;T&gt; thingToProcess)。函数TranslateThing 具有签名val TranslateThing : (Guid -&gt; Thing&lt;'T&gt; -&gt; TranslatedThing&lt;'T&gt;)。这有帮助吗?

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&lt;'T&gt; 的签名在哪里:

type SpecificThingTranslator<'T> =
 {First: TranslatedThing<'T>;
  Second: Lazy<TranslatedThing<'T>>;
  Third: Lazy<TranslatedThing<'T>>;
  Fourth: Lazy<TranslatedThing<'T>>;}

删除cacheTwo 行并没有解决错误,因为函数TranslateThing 最终引用cacheTwo。消除对cacheTwo 的所有引用可以消除错误。

我可能会找到将Thing&lt;'T&gt; 映射到SpecificThingTranslator&lt;'T&gt; 的解决方法。不过,我在这里错过了什么吗?我是否忘记了允许此映射的 .NET 集合(或者可能是特定于 F# 的集合)?虽然每对的键和值的类型参数必须相同,但每个 KeyValuePair(或等效项)可以有不同的类型参数。

【问题讨论】:

  • 一旦有明确的解决方案,我会在时间允许的情况下编辑问题以简洁明了。编译器错误对于 F# 新手来说有点神秘,而且 Google-the-error-message 方法没有产生任何似乎适用的结果。我希望其他遇到错误信息的程序员可以从这个问答中受益。

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


【解决方案1】:

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

type Thing<'T> = T of 'T

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

我删除了 public 修饰符,因为这是 F# 中成员的默认值,我还删除了额外的括号,但除此之外,什么都没有改变......

【讨论】:

  • 很有可能。我会添加更多细节,看看是否有帮助。
  • 是的,如果你能给出一个给出错误的最小样本,那么就有可能给出一些更有用的答案!
  • 谢谢。我已经添加了代码,但它可能不优雅。正如您可能猜到的那样,我对 F# 还很陌生。
  • @Andrew 即使使用更新的版本,我仍然无法复制错误(我刚刚添加了所有必要的定义以使您的代码编译...)所以,您可能还需要添加更多(或简化您的代码以找到问题的根源..)
  • 再次感谢您试一试。今天晚上我会尝试提炼这段代码。
猜你喜欢
  • 2011-10-04
  • 2011-01-29
  • 1970-01-01
  • 2011-03-07
  • 1970-01-01
  • 1970-01-01
  • 2012-12-19
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多