我试图在 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(或等效项)可以有不同的类型参数。