ということは、これらは CER 内に含められないし、呼び出し元は Cer.None になってしまう、ということだろーか…?
本来、GC.AddMemoryPressure には Cer.MayFail か Cer.Success が、GC.RemoveMemoryPressure には Cer.Success がマークされてるべきじゃないの??
null許容型は参照型と値型のどちらになるの?
C# だとローカル変数への代入は値渡ししかできません。
参照渡しで代入を行うようなコードを C# っぽく書いてみます。
Sample.cs
class Program
{
static void Main()
{
int v0 = 0;
int& v1 = v0;
v0 = 5;
System.Console.WriteLine(v0);
System.Console.WriteLine(v1);
}
}
このコードが動作するとすれば、v1 への代入が参照渡しになるので v1 からも 5 が取得されるはずですが、当然ながらこのコードはそもそもコンパイルできません。
しかし、IL ならばローカル変数への参照渡しができます。
先ほどのコードと同等のコードを IL で書くと次のようになります。
Sample.il
.assembly extern mscorlib { }
.assembly sample { }
.class private abstract auto ansi sealed beforefieldinit Program
extends [mscorlib]System.Object
{
.method private hidebysig static void Main(string[] args) cil managed
{
.entrypoint
.locals init
(
[0] valuetype [mscorlib]System.Int32 v0,
[1] valuetype [mscorlib]System.Int32& v1
)
// v0 = 0;
ldc.i4.0
stloc.0
// v1 = v0;
ldloca.s v0
stloc.1
// v0 = 5;
ldc.i4.5
stloc.0
// Console.WriteLine(v0);
ldloc.0
call void [mscorlib]System.Console::WriteLine(int32)
// Console.WriteLine(v2);
ldloc.1
ldind.i4
call void [mscorlib]System.Console::WriteLine(int32)
ret
}
}
このコードを次のコマンドで exe ファイルとしてアセンブルします。
ilasm Sample.il
これを実行すると、v0 も v1 も 5 を返すことが確認できます。
つまり、v1 への参照渡しによる代入が可能であることが確認できます。
…まぁ、だからどうというわけでもないんですが。
IL で可能なので、C++/CLI でもできるかもしれませんね。
using System;
using System.Dynamic;
using System.Reflection;
namespace MoroMoro.Dynamic
{
public sealed class MemberAdapter : DynamicObject
{
private static BindingFlags GetBindingFlags(bool ignoreCase)
{
var baseFlags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.FlattenHierarchy;
return (ignoreCase ? (baseFlags | BindingFlags.IgnoreCase) : baseFlags);
}
public MemberAdapter(object target)
{
if (target == null)
{
throw new ArgumentNullException("target");
}
this._target = target;
}
private readonly object _target;
private Type TargetType
{
get
{
return this._target.GetType();
}
}
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
var flags = GetBindingFlags(binder.IgnoreCase);
var field = TargetType.GetField(binder.Name, flags);
var property = TargetType.GetProperty(binder.Name, flags);
if (field != null)
{
result = field.GetValue(this._target);
return true;
}
else if ((property != null) && (property.CanRead))
{
result = property.GetValue(this._target, null);
return true;
}
else
{
result = null;
return false;
}
}
public override bool TrySetMember(SetMemberBinder binder, object value)
{
var flags = GetBindingFlags(binder.IgnoreCase);
var field = TargetType.GetField(binder.Name, flags);
var property = TargetType.GetProperty(binder.Name, flags);
if (field != null)
{
field.SetValue(this._target, value);
return true;
}
else if ((property != null) && (property.CanRead))
{
property.SetValue(this._target, value, null);
return true;
}
else
{
return false;
}
}
public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result)
{
var flags = GetBindingFlags(binder.IgnoreCase);
var filter = (MemberFilter)((member, c) =>
{
var memberAsMethod = member as MethodInfo;
if (memberAsMethod == null)
{
return false;
}
if (!string.Equals(memberAsMethod.Name, binder.Name, (binder.IgnoreCase ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal)))
{
return false;
}
var parameters = memberAsMethod.GetParameters();
if (args.Length != parameters.Length)
{
return false;
}
for (var i = 0; i < parameters.Length; i++)
{
var parameter = parameters[i];
var arg = args[i];
if (arg == null)
{
continue;
}
if (parameter.ParameterType.IsAssignableFrom(arg.GetType()))
{
continue;
}
return false;
}
return true;
});
var methods = TargetType.FindMembers(MemberTypes.Method, flags, filter, null);
if ((methods == null) || (methods.Length == 0))
{
result = null;
return false;
}
var method = (MethodInfo)methods[0];
result = method.Invoke(this._target, args);
return true;
}
}
}
サンプル
class Program
{
class Sample
{
public Sample()
{
this._i = 0;
}
private readonly int _i;
private void Method1()
{
Console.WriteLine(_i);
}
}
static void Main()
{
var s = new Sample();
dynamic m = new MemberAdapter(s);
m._i++;
m.Method1();
Console.ReadKey();
}
}
事実上、.NET 4.0 からは構文ツリーに進化したらしい。
例えば、こんな感じのプログラムを式ツリーで構築したいとする。
for (int i = 1; i <= 10; i++)
{
Console.WriteLine(i);
}
式ツリーでループを組むことはできるが for 文を直接組むことはできないので、こんな感じに変形する。
int i = 1;
while (true)
{
if (i <= 10)
{
Console.WriteLine(i);
}
else
{
break;
}
i++;
}
これを式ツリーで組むとこんな感じになる。
var breakLabel = Expression.Label();
var iParameter = Expression.Variable(typeof(int), "i");
var writeLineMethod = typeof(Console).GetMethod("WriteLine", new[] { typeof(int) });
var lambda = Expression.Lambda
(
Expression.Block
(
new[]
{
iParameter
},
Expression.Assign(iParameter, Expression.Constant(1)),
Expression.Loop
(
Expression.Block
(
Expression.IfThenElse
(
Expression.LessThanOrEqual(iParameter, Expression.Constant(10)),
Expression.Call(writeLineMethod, iParameter),
Expression.Break(breakLabel)
),
Expression.PostIncrementAssign(iParameter)
),
breakLabel
)
)
);
var @delegate = lambda.Compile();
@delegate.DynamicInvoke();
式ツリーを Compile すると DynamicMethod が内部で作られるわけだけど、式ツリーを使わず自分で直接 DynamicMethod を構築するとなると IL を組み立てていくことになるので一苦労。IL だから for 文どころかループもないので、ラベルと goto で…。
ちなみに、CompileToMethod を使用すれば動的クラスの静的メソッドも構築できる。残念ながら動的クラスのインスタンスメソッドを構築することはできない。
最後に、AST (IronPython 同梱の Microsoft.Dynamic.dll に含まれている) を使用すると for 文が簡単に作れるので結構すっきりする。
var writeLineMethod = typeof(Console).GetMethod("WriteLine", new[] { typeof(int) });
var builder = Utils.Lambda(typeof(void), string.Empty);
var iParameter = builder.Variable(typeof(int), "i");
builder.Body = Expression.Block
(
Expression.Assign(iParameter, Expression.Constant(1)),
Utils.Loop
(
Expression.LessThanOrEqual(iParameter, Expression.Constant(10)),
Expression.PostIncrementAssign(iParameter),
Expression.Call(writeLineMethod, iParameter),
null
)
);
var lambda = builder.MakeLambda();
var @delegate = lambda.Compile();
@delegate.DynamicInvoke();