型情報
クラス名やメンバ名等の情報をメタデータ(型情報)と呼び、プログラムの実行時にメタデータを取り出すための機能をリフレクション(reflection)と呼ぶ。リフレクションで得られる型情報のことを実行時型情報(Runtime Type Information)と呼ぶことがある。
実行時型情報
リフレクションを使用することで、文字列から動的にインスタンスを生成することができる。ただし、リフレクションを使った処理は、通常の処理に比べると数千倍ほど遅いため、注意が必要。
using System;
namespace Sample
{
class Program
{
static void Main(string[] args)
{
Rect x = new Rect();
x.Height = 3;
x.Width = 4;
Console.WriteLine("area: {0}", x.Height * x.Width);
Type t = Type.GetType("Sample.Rect");
object o = Activator.CreateInstance(t);
t.GetField("Width").SetValue(o, 3);
t.GetField("Height").SetValue(o, 4);
Console.WriteLine(
"area: {0}",
(int)t.GetField("Width").GetValue(o) * (int)t.GetField("Height").GetValue(o)
);
}
}
struct Rect
{
public int Height;
public int Width;
}
}
属性
属性(attribute)とは、クラスやメンバーに追加の実行時情報を与えるものである。属性の情報は以下のような場面で使われる。
- 条件コンパイルなどの、コンパイラへの指示に使う(Conditional, Obsolete)
- 作者情報などをメタデータとしてプログラムに埋め込む(AssemblyTitle)
- リフレクションを利用して、プログラム実行時に属性情報を取り出して利用する
以下のコード例では、Conditional属性を使用している。SHOW_INTERMEDIATEというシンボルが定義されているときはInterMediateOutputメソッドが実行される。
#define SHOW_INTERMEDIATE
using System;
using System.Diagnostics;
namespace Sample
{
class Program
{
static void Main(string[] args)
{
BubbleSort(new double[] { 4.5, 0.1, 1.2, 2.3, });
}
static void BubbleSort(double[] array)
{
int n = array.Length - 1;
for (int i = 0; i < n; ++i)
{
for (int j = n; j > i; --j)
{
if (array[j - 1] > array[j])
{
Swap(ref array[j - 1], ref array[j]);
}
}
InterMediateOutput(array);
}
}
static void Swap(ref double x, ref double y)
{
double tmp = x;
x = y;
y = tmp;
}
static void Output(double[] array)
{
foreach (double x in array)
{
Console.Write("{0}, ", x);
}
Console.Write("n");
}
[Conditional("SHOW_INTERMEDIATE")]
static void InterMediateOutput(double[] array)
{
Output(array);
}
}
}
式ツリー
ラムダ式をExpression型の変数に代入すると、式ツリーデータとして扱うことができる。式ツリーデータとして扱えるのは単文のみ。
式ツリーは、Compileメソッドを呼び出すことで動的にコンパイルして実行することができる。
動的変数
C#4.0で、動的型付け変数(dynamic型)が追加された。
C#のdynamicは、「型が動的」というよりは、「静的な型に対する動的コード生成」といったほうが正しい。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApplication1
{
struct Point2D
{
public int X, Y;
}
struct Point3D
{
public int X, Y, Z;
}
class Program
{
static void Main(string[] args)
{
Console.WriteLine(Sum(new Point2D { X = 1, Y = 2 }));
Console.WriteLine(Sum(new Point3D { X = 2, Y = 3, Z = 4 }));
Console.WriteLine(Sum(new { X = 3, Y = 4 }));
}
static int Sum(dynamic obj)
{
return (int)(obj.Y + obj.X);
}
}
}
リフレクションやdynamic型は「補足資料」の位置づけでも良いと思うけど、属性はC#でアプリケーションを書くなら絶対に知っておく必要がある事項なので、補足資料に回されてしまっているのは疑問。