タイプExpression<Func<TModel, TProperty>>
は、「タイプ TModel の着信オブジェクトを処理して、タイプ TProperty の他のオブジェクトを返す命令」です。ここ:
public static MvcHtmlString TextBoxFor<TModel, TProperty>
(this HtmlHelper<TModel> htmlHelper,
Expression<Func<TModel, TProperty>> expression)
{
return htmlHelper.TextBoxFor(expression, format: null);
}
HtmlHelper の TextBoxFor 拡張メソッドには、上記のようなパラメーターが必要ですが、式の引数は、HtmlHelper が作成されたのと同じ型、つまり TModel であり、TProperty 型のオブジェクトを返す必要があります。
実際のパラメーターm => m.SomeValue
は、「受信した m の SomeValue プロパティを返すだけ」と同じですが、「"foo" を返す」または「null を返す」または「新しい WeirdObject() を返す」の場合もあります。
メソッドTextBoxFor
はオーバーロード メソッドのみを呼び出します。
更新
まず、「ジェネリック型」に関する MSDN と Google の記事 ( this とthisのように)) 私よりもはるかにうまく説明できます。このメカニズムを使用して、インスタンスの作成時に提供された型に固有のクラスまたはメソッドを作成し、たとえば、一般的な「オブジェクト」ではなく同じ型の結果を返すことができるようにします。任意の型の値のリストを保持でき、結果としてそれらのすべての秒を返すことができるList
クラスを (Microsoft が行う前に)
作成したいとします。私はこのようにすることができます:
public class List{
public IEnumerable Items; // collection of "objects"
public IEnumerable GetEvenItems(){
// some implementation returning another "objects" collection
}
}
int、文字列、性別などのリストを維持するために使用できますが、GetEvenItemsは常に「オブジェクト」を返すため、より具体的に作業を続けるには、それらを元の型にキャストする必要があります。これの代わりに私は別のクラスを作ります:
public class List<T>{ }
そして、これにより、「プログラマーは目的の型を指定する必要があるため、クラス内で常に認識されるため、いつでも値をキャストできます」。タイプがわかったので、使えます。例えば:
public class List<T>{
public T[] Items; // collection of strongly typed values
public T[] GetEvenItems(){
// some implementation returning typed collection
}
}
これにより、アイテムは作成時に提供された特定のタイプのものになると言えます。また、私の Item とGetEvenItemsは特定のタイプのアイテムも返すので、整数または文字列のコレクションとしてそれらを操作できます。外部コードが myList.GetEvenItems を呼び出すと、メソッドが T の配列を返すことがわかります。ちなみに、Tの代わりに他の名前を使用できます。これは単なる「型変数」です。Tの代わりにTModelまたはTMyThoughtsを宣言に入れることができます。
可能なタイプを制限することもできます。私のメソッドDoTheWebJobは、 IControllerのみで動作できるとします。
タイプ。次に、追加の制約を提供します。
public class MyClass<T> where T: IController
{
public T[] Items;
public void DoTheWebJob()
{
Items[0].Execute(null);
}
}
つまり、クラスに指定できるのはIControllerの子孫のみです。私のクラス本体はTがIControllerであることを既に「知っている」ので、IController固有のメソッドを簡単に呼び出すことができます。
また、プログラマーが次のように複数のタイプを提供する必要があるように、クラスまたはメソッドを設計することもできます。
public class List<T1, T2>{ }
ここまでは順調ですね。行きましょう
System.Web.Mvc.HtmlHelper<T>
これはちょうど私たちのようなものList<T>
です.: インスタンスを作成するとき、プログラマーは実際のT値を次のように指定します:
HtmlHelper<int> myHelper = new HtmlHelper<int>();
HTMLタグをレンダリングする独自のヘルパーが欲しいとしましょう。
public class MyHtmlHelper<T>
{
public string RenderSpan(string name, object value)
{
return String.Format("<span id=\"{0}\">{1}</span>", name, value.ToString());
}
}
SPAN
これはクールで、params を使用してタグをレンダリングし、paramsname
で提供できます。value
だから私は何でも入れることができ、見栄えがよくなりますSPAN
。クラス宣言にジェネリックはまったく必要ありません。
ここで、レンダラーを変更して、SPAN
のID
属性をオブジェクトのプロパティの名前として設定します。Idプロパティを持つProductオブジェクトがあるとします。Productをレンダラーに渡したいので、のID ="Id" と内部の html をIdの値 (たとえば 5) として設定します。レンダラーはどのようにしてプロパティIdの名前を知ることができますか? 単純にProduct.Idを渡すとSPAN
、これは単なる整数値になり、レンダラーはこのプロパティ名が何であるかわからず、設定できませんSPAN ID=
...
まあ、式の力が私たちを助けます. まずFunc<T1, T2>
、T1 型のパラメーターを受け取り、T2 型の結果を返すデリゲートです。Expression<Func<T1, T2>>
デリゲートのロジックを記述する式ですFunc<T1, T2>
。したがって、デリゲート自体に簡単にコンパイルできますが、逆方向にはコンパイルできません。
次のコードを記述します。
internal class Program
{
public class Entity
{
public int Id { get; set; }
}
private static void Main(string[] args)
{
Expression<Func<Entity, int>> fn = e => e.Id;
// breakpoint here
}
}
ブレークポイントを設定し、fn.Body データ型を監視します。これはPropertyExpressionになります。つまり、システムは「オブジェクトのe => e.Id
このプロパティを取得する」と解析しますが、「Id の値を返す」とは解析しません。これ以降、ボディはこれを何らかのオブジェクトのプロパティとして「考え」、その名前と値の両方を読み取ることができます。このような式を使用して、レンダラーにプロパティ名を認識させ、レンダリングできるようにしますSPAN
。
public class MyHtmlHelper<T>
{
public string RenderSpan(string name, object value)
{
return String.Format("<span name=\"{0}\">{1}</span>", name, value.ToString());
}
public string RenderSpan(System.Linq.Expressions.PropertyExpression pe)
{
// extract property name and value and render SPAN here
}
public string RenderSpan(Expression<Func<object, object>> expr)
{
// if specified expr was like x => x.Id then it will actually be parsed like PropertyExpression in above
}
}
ただし、クラス宣言には既にエンティティ型Tがあり、この型を使用できます。したがって、最後のメソッドを次のように変更できます。
public string RenderSpan(Expression<Func<T, object>> expr)
{
// if specified expr was like x => x.Id then it will actually be parsed like PropertyExpression in above
}
つまり、htmlHelperHtmlHelper<MyModel>
がタイプとして作成された場合、 RenderSpanには式が必要になりExpression<Func<MyModel, object>>
ます。例: myModel => myModel.Id;
。cshtml ファイルでTextBoxForを作成しようとすると、以前と同じタイプが必要であることがわかります@model
。これは、実際の html ヘルパーが暗黙的に として作成されたためnew HtmlHelper<MyModel>()
です。ここで、RenderSpanがTがHtmlHelperの作成に使用された型であることを認識すると、 の右側の部分でTのプロパティを使用できるようになりますx => x.Id
。T =エンティティを認識しており、 「 x.Id 」と言うことができます。もし表現がobject, object
その後、これを行うことはできません。Microsoft の宣言では、 Tの代わりにTModelを使用して、それが何であるかを直感的に理解できるようにしています。
さて、簡単に言うと:
1. cshtml に書き込みます
2. MVC はヘルパーを作成します
3. #2 は式の入力引数がMyModel型であることを認識しているため、そのプロパティで操作でき、入力したものを使用できます式の右側の部分TProperty
として 2 番目の型パラメーターが必要な理由がわかりません。それは単にobjectである可能性があります。おそらくそれはTextBoxForメソッドでより深く伝播されます。
@model MyModel
HtmlHelper<MyModel>
SpanFor
TextBoxFor