你可以创建一个实例方法的委托,而不在创建时指定的实例? 换句话说,你可以创建一个“静态”的委托,它需要,因为它是第一个参数实例的方法应该叫上?
例如,如何使用反射我构建了以下委托?
Func<int, string> = i=>i.ToString();
我知道的事实,我可以使用methodInfo.Invoke,但这是慢,不检查类型的正确性,直到它被调用。
当你有MethodInfo
特定静态方法,可以构建使用委托Delegate.CreateDelegate(delegateType, methodInfo)
,和静态方法的所有参数保持自由。
作为乔恩斯基特指出的那样,你可以简单地将相同的制作实例方法的开放委托,如果该方法是引用类型非虚。 决定在虚拟方法调用哪个方法是棘手的,所以这是没有那么微不足道,和值类型看起来他们没有在所有的工作。
对于值类型, CreateDelegate
表现出很奇怪的行为:
var func37 = (Func<CultureInfo,string>)(37.ToString);
var toStringMethod = typeof(int).GetMethod("ToString", BindingFlags.Instance | BindingFlags.Public, null, new Type[] {typeof(CultureInfo) }, null);
var func42 = (Func<CultureInfo,string>)Delegate.CreateDelegate(typeof(Func<CultureInfo,string>), 42, toStringMethod,true);
Console.WriteLine( object.ReferenceEquals(func37.Method,func42.Method)); //true
Console.WriteLine(func37.Target);//37
Console.WriteLine(func42.Target);//42
Console.WriteLine(func37(CultureInfo.InvariantCulture));//37
Console.WriteLine(func42(CultureInfo.InvariantCulture));//-201040128... WTF?
主叫CreateDelegate
与null
为目标对象抛出的结合异常,如果实例方法属于一个值类型(这适用于引用类型)。
一些后续年后:导致不正确绑定目标func42(CultureInfo.InvariantCulture);
返回"-201040128"
而不是"42"
在我的例子是存储器讹误能允许远程执行代码( CVE-2010至1898年 ); 这是在固定在2010 MS10-060安全更新。 当前框架正确打印42! 这不作任何容易回答这个问题,但解释的例子中,特别怪异的行为。
你实际上已经选择了一个特别棘手的例子,有两个原因:
- 的ToString()是从继承虚拟方法
object
,但在覆盖Int32
。 -
int
是一个值类型,并有规则的怪异与Delegate.CreateDelegate()
当涉及到的值类型和实例方法-基本上与第一有效参数变为ref int
而非int
但是,这里有一个例子String.ToUpper
,不具有任何的这些问题:
using System;
using System.Reflection;
class Test
{
static void Main()
{
MethodInfo method = typeof(string).GetMethod
("ToUpper", BindingFlags.Instance | BindingFlags.Public,
null, new Type[]{}, null);
Func<string, string> func = (Func<string, string>)
Delegate.CreateDelegate(typeof(Func<string, string>),
null,
method);
string x = func("hello");
Console.WriteLine(x);
}
}
如果这足以让你很好的,伟大的...如果你真的想int.ToString
,我要尝试有点困难:)
下面是一个值类型的例子,使用一个新的委托类型,其通过引用将其第一个参数:
using System;
using System.Reflection;
public struct Foo
{
readonly string value;
public Foo(string value)
{
this.value = value;
}
public string DemoMethod()
{
return value;
}
}
class Test
{
delegate TResult RefFunc<TArg, TResult>(ref TArg arg);
static void Main()
{
MethodInfo method = typeof(Foo).GetMethod
("DemoMethod", BindingFlags.Instance | BindingFlags.Public,
null, new Type[]{}, null);
RefFunc<Foo, string> func = (RefFunc<Foo, string>)
Delegate.CreateDelegate(typeof(RefFunc<Foo, string>),
null,
method);
Foo y = new Foo("hello");
string x = func(ref y);
Console.WriteLine(x);
}
}
你可以使用Lambda表达式得到您的实例方法“有点”编译静态包装。
下面的示例是不完全的最快的,但它应该比任何纯动态调用显著更快。
输出
100000 iterations took 4 ms
1000000 iterations took 18 ms
10000000 iterations took 184 ms
代码
class Program
{
public sealed class Test
{
public String Data { get; set; }
public override string ToString()
{
return Data;
}
}
static void Main(string[] args)
{
TestRun(100000);
TestRun(1000000);
TestRun(10000000);
}
private static void TestRun(int iterations)
{
var toString = typeof(Test).GetMethod("ToString",
BindingFlags.Instance
| BindingFlags.Public,
null,
Type.EmptyTypes,
null);
var call = GetCall<Test, String>(toString);
var tests
= (from i in Enumerable.Range(1, iterations)
select new Test { Data = "..." + i }).ToList();
var sw = Stopwatch.StartNew();
tests.ForEach(i => call(i));
sw.Stop();
Console.WriteLine("{0} iterations took {1} ms", iterations, sw.ElapsedMilliseconds);
}
private static Func<T, M> GetCall<T, M>(MethodInfo methodInfo)
{
var input = Expression.Parameter(typeof(T), "input");
MethodCallExpression member = Expression.Call(input, methodInfo);
var lambda = Expression.Lambda<Func<T, M>>(member, input);
return lambda.Compile();
}
}
在goog方式也许可以期运用“动态”类型在.NET 4.0中。 然而,代表需要的实例(非静态方法)。 这些问题是比较复杂的,然后在第一次因为polymorfism等lokks ...