12

F#でダックタイピングを実装しようとしていますが、F#ジェネリックで次のよう にメンバー制約を設定できることに気付きました。

type ListEntryViewModel<'T when 'T : (member Name : string)>(model:'T) = 
  inherit ViewModelBase()

  member this.Name with get() = model.Name

ただし、プロパティを参照しようとすると、上記のコードはコンパイルされません。コンパイラエラーが発生します:

このコードは十分に一般的ではありません。^ T:(member get_Name:^ T-> string)の場合の型変数^ Tは、スコープをエスケープするため、一般化できませんでした。

ジェネリック制約を介してダックタイピングを実装することは可能ですか?

4

3 に答える 3

25

最近、型宣言でメンバー制約が使用されているという同様の質問がありました。

サンプルを修正してコンパイルする方法はわかりませんが、それが不可能な場合でも驚かないでしょう。メンバー制約は、静的に解決された型パラメーター、特にinline関数またはメンバーで使用するように設計されており、クラスの型パラメーターで使用するのは慣用的なF#コードではないと思います。

あなたの例に対するより慣用的な解決策は、インターフェースを定義することだと思います。

type INamed = 
  abstract Name : string

type ListEntryViewModel<'T when 'T :> INamed>(model:'T) =  
  member this.Name = model.Name

(実際、ListEntryViewModelおそらく型パラメーターは必要なくINamed、コンストラクターパラメーターとして使用できますが、このように記述することにはいくつかの利点があるかもしれません。)

これで、ダックタイピングを使用ListEntryViewModelして、プロパティを持つもので使用できますが、インターフェイスNameは実装しません。INamedこれは、静的メンバー制約をinline返し、使用して既存のプロパティをキャプチャする関数を作成することで実行できます。INamedName

let inline namedModel< ^T when ^T : (member Name : string)> (model:^T)= 
  { new INamed with
      member x.Name = 
        (^T : (member Name : string) model) }

次に、インターフェイスを実装する必要はなく、プロパティのみが必要なListEntryViewModel(namedModel someObj)場所を記述して、ビューモデルを作成できます。someObjName

インターフェイスを使用することで、モデルから必要なものをより適切に文書化できるため、このスタイルをお勧めします。スキームに適合しない他のオブジェクトがある場合は、それらを適応させることができますが、モデルを作成している場合は、インターフェイスを実装することで、必要なすべての機能を確実に公開できます。

于 2012-10-22T13:02:27.593 に答える
6

ジェネリック制約を介してダックタイピングを実装することは可能ですか?

いいえ。いくつかの特殊なケースを除いて、F#はダックタイピングが不可能な記名的型付けのみを実装しています。他の回答で説明されているように、慣用的な「解決策」は、インターフェイスをそのインターフェイスに準拠させたいすべてのクラスに後付けすることですが、もちろん、ダックタイピングが必要なほとんどの場合は実用的ではありません。

F#のこの制限は、.NETから継承されていることに注意してください。ダックタイピングに似たより実用的な解決策を見たい場合は、OCamlの構造的に型付けされたポリモーフィックバリアントとオブジェクトをチェックしてください。

于 2012-10-23T08:54:23.210 に答える
5

元のコードを機能させるには:

type ListEntryViewModel< ^T when ^T : (member Name : string)>(model:^T) = 
    inherit ViewModelBase()

    member inline this.Name with get() = (^T : (member Name : string) model)

したがって、メンバーを「インライン」としてマークし、メンバー関数で制約を繰り返す必要があります。

F#では通常インターフェイスベースのアプローチが好まれるというTomasに同意します。

于 2012-10-22T17:04:07.363 に答える