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# アセンブリで定義されており、メソッドは 1 つだけ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
wherecacheTwo
は、クラスで以前に定義されています。
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 (または同等のもの) は異なる型パラメーターを持つことができます。