1

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もエラーは解決しませんでした。へのすべての参照を削除すると、エラーが解消されます。TranslateThingcacheTwocacheTwo

Thing<'T>にマップする回避策をおそらく見つけることができますSpecificThingTranslator<'T>。それにもかかわらず、私はここで何かを逃しましたか? このマッピングを可能にする .NET コレクション (または F# 固有のコレクション) を忘れていませんか? 各ペアのキーと値の型パラメーターは同じでなければなりませんが、各 KeyValuePair (または同等のもの) は異なる型パラメーターを持つことができます。

4

1 に答える 1

7

コードの別の部分に問題があるはずです。次の最小限のサンプルは、まったく同じ定義をMyMethod持ち、正常に動作します (新しいスクリプト ファイルに貼り付けた場合)。

type Thing<'T> = T of 'T

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

F# のメンバーのデフォルトであるため、修飾子を削除し、public追加の括弧も削除しましたが、それ以外は何も変更されていません....

于 2013-06-23T21:26:43.950 に答える