textBox1.Text = myc.MyMethodDummy();
一方、dynamic型の場合は、型チェックが実行時のため、クラスに存在しないメソッドを記述してもコンパイルエラーは発生しません。
textBox1.Text = dyn.MyMethodDummy();
ラムダ式を利用してクラスのメソッドを動的に呼び出すコードを紹介します。
はじめにラムダ式など利用せず、dynamic型を利用した呼び出しコードを紹介します。
Windows Formアプリケーションを作成します。
下図のフォームを作成します。
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Reflection;
using System.Reflection.Emit;
using System.Linq.Expressions;
namespace DynamicDelegate
{
public class MyClass
{
public string MyMethod()
{
return "Hello!";
}
}
public partial class FormSimpleDynamicDelegate : Form
{
public FormSimpleDynamicDelegate()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
var myc = new MyClass();
dynamic dyn = myc;
textBox1.Text = dyn.MyMethod();
}
}
}
ボタンのクリックにより下記のコードが実行されます。
MyClassのインスタンスを作成します。var
で変数宣言していますので型推論により、MyClass型の変数となります。
var myc = new MyClass();
作成したインスタンスを格納しているMyClass型の値を dynamic型の変数に代入します。
dynamic dyn = myc;
dynamic型の変数に代入したMyClassインスタンスオブジェクトのMyMethod()
メソッドを呼び出し、結果をテキストボックスに表示します。MyMethod() メソッドは "Hello!"の文字列を返します。
textBox1.Text = dyn.MyMethod();
textBox1.Text = myc.MyMethodDummy();
textBox1.Text = dyn.MyMethodDummy();
上記のプロジェクトを実行します。下図のフォームが表示されます。
[button1]をクリックします。テキストボックスに "Hello!" の文字列が表示されます。~
Windows Formアプリケーションを作成します。
下図のフォームを作成します。先のプログラムのフォームにボタンを一つ追加したものになります。
下記のコードを記述します。
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Reflection;
using System.Reflection.Emit;
using System.Linq.Expressions;
namespace DynamicDelegate
{
public class MyClass
{
public string MyMethod()
{
return "Hello!";
}
}
public partial class FormSimpleDynamicDelegate : Form
{
public FormSimpleDynamicDelegate()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
}
private void button2_Click(object sender, EventArgs e)
{
var myc = new MyClass();
var callByEmit = CallByEmit<MyClass, string>("MyMethod");
var answerByEmit = callByEmit(myc);
textBox1.Text = answerByEmit.ToString();
}
static Func<T, TResult> CallByEmit<T, TResult>(string methodName)
{
DynamicMethod dmethod = new DynamicMethod(
"call",
typeof(string),
new[] { typeof(T) });
var item = dmethod.DefineParameter(position: 1, attributes: ParameterAttributes.In, parameterName: "item");
var generator = dmethod.GetILGenerator();
generator.Emit(opcode: OpCodes.Ldarg_0);
generator.Emit(opcode: OpCodes.Callvirt, meth: typeof(T).GetMethod(name: methodName, types: Type.EmptyTypes));
generator.Emit(opcode: OpCodes.Ret);
return (Func<T, TResult>)dmethod.CreateDelegate(delegateType: typeof(Func<T, TResult>));
}
}
}
[button2]をクリックすると以下の処理を実行します。
MyClass クラスのインスタンスを作成します。
var myc = new MyClass();
CallByEmitメソッドを呼び出し、メソッドのデリゲートを取得します。取得するメソッド名を引数に与えます。
var callByEmit = CallByEmit<MyClass, string>("MyMethod");
CallByEmit メソッドで取得したデリゲートを呼び出します。デリゲートを呼び出すことで、MyClassオブジェクトのMyMethodメソッドを呼び出す処理になります。
メソッドの戻り値はデリゲートの戻り値になります。
var answerByEmit = callByEmit(myc);
メソッドの戻り値をテキストボックスに表示します。
textBox1.Text = answerByEmit.ToString();
デリゲートの取得メソッドは下記コードです。DynamicMethod オブジェクトを利用してクラスのデリゲートを取得します。
static Func<T, TResult> CallByEmit<T, TResult>(string methodName)
{
DynamicMethod dmethod = new DynamicMethod(
"call",
typeof(string),
new[] { typeof(T) });
var item = dmethod.DefineParameter(position: 1, attributes: ParameterAttributes.In, parameterName: "item");
var generator = dmethod.GetILGenerator();
generator.Emit(opcode: OpCodes.Ldarg_0);
generator.Emit(opcode: OpCodes.Callvirt, meth: typeof(T).GetMethod(name: methodName, types: Type.EmptyTypes));
generator.Emit(opcode: OpCodes.Ret);
return (Func<T, TResult>)dmethod.CreateDelegate(delegateType: typeof(Func<T, TResult>));
}
上記のプロジェクトを実行します。下図のフォームが表示されます。
[button2]をクリックします。テキストボックスに "Hello!" の文字列が表示されます。~
先ほどのプログラムでは、デリゲートを動的に生成するため、CreateDelegate メソッドを呼び出しましたが、
このプログラムではラムダ式を利用した、シンプルな記述コードを紹介します。
下図のUIを作成します。
下記のコードを記述します。
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Reflection;
using System.Reflection.Emit;
using System.Linq.Expressions;
namespace DynamicDelegate
{
public class MyClass
{
public string MyMethod()
{
return "Hello!";
}
}
public partial class FormSimpleDynamicDelegate : Form
{
public FormSimpleDynamicDelegate()
{
InitializeComponent();
}
private void button3_Click(object sender, EventArgs e)
{
var myc = new MyClass();
var callByExpression = CallByExpression<MyClass, string>("MyMethod");
var answerByExpression = callByExpression(myc);
textBox1.Text = answerByExpression.ToString();
}
static Func<T, TResult> CallByExpression<T, TResult>(string methodName)
{
// (T item) => item.methodName()
var parameterExpression = Expression.Parameter(type: typeof(T), name: "item");
var callExpression = Expression.Call(
instance: parameterExpression,
method: typeof(T).GetMethod(methodName, Type.EmptyTypes)
);
var lambda = Expression.Lambda(callExpression, parameterExpression);
return (Func<T, TResult>)lambda.Compile();
}
}
}
[button3]がクリックされた際に実行される下記のコードは、先のCreateDelegate を利用する場合のコードとほぼ同様です。
MyClassインスタンスを生成し、CallByExpression
メソッドで呼び出しメソッドのデリゲートを取得します。CallByExpression
メソッドの引数に
取得したいクラスメソッドの名前を与えます。
取得したデリゲートを呼び出すことで、MyClassの MyMethod
メソッドを呼び出す動作になります。
var myc = new MyClass();
var callByExpression = CallByExpression<MyClass, string>("MyMethod");
var answerByExpression = callByExpression(myc);
textBox1.Text = answerByExpression.ToString();
デリゲートを返すCallByExpression
メソッドのコードが下記です。CallByExpression
メソッドでは、CreateDelegate は利用せず、
ラムダ式を返すことでデリゲートを返します。
生成されるラムダ式は次のものになります。
(T item) => item.methodName()
Expression.Parameter メソッドを利用してラムダ式のパラメータを作成します。また、Expression.Call メソッドを利用してラムダ式の呼び出しで
メソッドの呼び出しをするラムダ式を作成します。
ラムダ式の作成は、Expression.Lambda メソッドで作成します。メソッドの引数に、呼び出しの定義callExpression
とパラメーターの定義 parameterExpression
を与えます。ラムダ式のオブジェクトはLambda メソッドの戻り値になります。
ラムダ式のオブジェクトの Complie
メソッドを呼び出しデリゲートを作成します。CallByExpression メソッドの戻り値として作成したデリゲートを返します。
static Func<T, TResult> CallByExpression<T, TResult>(string methodName)
{
var parameterExpression = Expression.Parameter(type: typeof(T), name: "item");
var callExpression = Expression.Call(
instance: parameterExpression,
method: typeof(T).GetMethod(methodName, Type.EmptyTypes)
);
var lambda = Expression.Lambda(callExpression, parameterExpression);
return (Func<T, TResult>)lambda.Compile();
}