标题基本上是我的问题。在封面下是最后一行:
Type xmlCodecType = typeof(XmlCodec<>).MakeGenericType(typeof(SomeObjectProperty));
dynamic xmlCodec = Activator.CreateInstance(xmlCodecType);
xmlCodec.ReadCollection(xmlCodec.GetCollectionName());
基本上这样做:
MethodInfo method1 = xmlCodec.GetType().GetMethod("ReadCollection");
MethodInfo method2 = xmlCodec.GetType().GetMethod("GetCollectionName");
method1.Invoke(xmlCodec, new obj[] { method2.Invoke(xmlCodec, null) });
何时执行??
我写的大部分内容都是使用反射方法,因为它是我习惯的,并且随着写入类型和对象等的传递,感觉更多的"在编译时捕获错误"。动态更放手一点。然而,反射有时可能更难阅读/遵循,而虽然动态并不总是语言中的关键字,但它是大多数语言的共同概念。
下面是一个简单的测试代码:
void Main()
{
this.DoSomething();
}
private void DoSomething()
{
Console.WriteLine("Foo");
}
编译为此 IL:
IL_0000: nop
IL_0001: ldarg.0
IL_0002: call UserQuery.DoSomething
IL_0007: nop
IL_0008: ret
DoSomething:
IL_0000: nop
IL_0001: ldstr "Foo"
IL_0006: call System.Console.WriteLine
IL_000B: nop
IL_000C: ret
现在,如果我介绍一个dynamic
参考:
void Main()
{
dynamic bar = this;
bar.DoSomething();
}
private void DoSomething()
{
Console.WriteLine("Foo");
}
下面是 IL:
IL_0000: nop
IL_0001: ldarg.0
IL_0002: stloc.0 // bar
IL_0003: ldsfld UserQuery+<Main>o__SiteContainer0.<>p__Site1
IL_0008: brtrue.s IL_0042
IL_000A: ldc.i4 00 01 00 00
IL_000F: ldstr "DoSomething"
IL_0014: ldnull
IL_0015: ldtoken UserQuery
IL_001A: call System.Type.GetTypeFromHandle
IL_001F: ldc.i4.1
IL_0020: newarr Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo
IL_0025: stloc.1 // CS$0$0000
IL_0026: ldloc.1 // CS$0$0000
IL_0027: ldc.i4.0
IL_0028: ldc.i4.0
IL_0029: ldnull
IL_002A: call Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo.Create
IL_002F: stelem.ref
IL_0030: ldloc.1 // CS$0$0000
IL_0031: call Microsoft.CSharp.RuntimeBinder.Binder.InvokeMember
IL_0036: call System.Runtime.CompilerServices.CallSite<System.Action<System.Runtime.CompilerServices.CallSite,System.Object>>.Create
IL_003B: stsfld UserQuery+<Main>o__SiteContainer0.<>p__Site1
IL_0040: br.s IL_0042
IL_0042: ldsfld UserQuery+<Main>o__SiteContainer0.<>p__Site1
IL_0047: ldfld System.Runtime.CompilerServices.CallSite<System.Action<System.Runtime.CompilerServices.CallSite,System.Object>>.Target
IL_004C: ldsfld UserQuery+<Main>o__SiteContainer0.<>p__Site1
IL_0051: ldloc.0 // bar
IL_0052: callvirt System.Action<System.Runtime.CompilerServices.CallSite,System.Object>.Invoke
IL_0057: nop
IL_0058: ret
DoSomething:
IL_0000: nop
IL_0001: ldstr "Foo"
IL_0006: call System.Console.WriteLine
IL_000B: nop
IL_000C: ret
这将反编译为:
private void Main()
{
object obj = (object)this;
if (SiteContainer.Site == null)
SiteContainer.Site = CallSite<Action<CallSite, object>>.Create(Microsoft.CSharp.RuntimeBinder.Binder.InvokeMember(CSharpBinderFlags.ResultDiscarded, "DoSomething", (IEnumerable<Type>)null, typeof(UserQuery), (IEnumerable<CSharpArgumentInfo>) new CSharpArgumentInfo[1]
{
CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, (string) null)
}));
SiteContainer.Site.Target((CallSite)SiteContainer.Site, obj);
}
private void DoSomething()
{
Console.WriteLine("Foo");
}
[CompilerGenerated]
private static class SiteContainer
{
public static CallSite<Action<CallSite, object>> Site;
}
所以,据我所知,这就是
为了补充其他答案,我进行了以下测试(更具体地说明您如何构建代码):
public class XmlCodec<T>
{
public List<string> MyList = new List<string>();
public string GetCollectionName()
{
return "Some Collection Name";
}
public void ReadCollection(string collectionName)
{
for (int i = 0; i < 100; i++)
MyList.Add(collectionName + i.ToString());
}
}
class Program
{
static void Main(string[] args)
{
Type xmlCodecType = typeof(XmlCodec<>).MakeGenericType(typeof(string));
dynamic xmlCodec = Activator.CreateInstance(xmlCodecType);
xmlCodec.ReadCollection(xmlCodec.GetCollectionName());
Print(xmlCodec);
Console.ReadKey(true);
}
public static void Print(dynamic obj)
{
foreach (string s in obj.MyList)
Console.WriteLine(s);
}
}
现在,在DotPeek中,它看起来像这样(为简洁起见,省略了打印方法):
private static void Main(string[] args)
{
object instance = Activator.CreateInstance(typeof (XmlCodec<>).MakeGenericType(typeof (string)));
// ISSUE: reference to a compiler-generated field
if (Program.u003CMainu003Eo__SiteContainer0.u003Cu003Ep__Site1 == null)
{
// ISSUE: reference to a compiler-generated field
Program.u003CMainu003Eo__SiteContainer0.u003Cu003Ep__Site1 = CallSite<Action<CallSite, object, object>>.Create(Binder.InvokeMember(CSharpBinderFlags.ResultDiscarded, "ReadCollection", (IEnumerable<Type>) null, typeof (Program), (IEnumerable<CSharpArgumentInfo>) new CSharpArgumentInfo[2]
{
CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, (string) null),
CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, (string) null)
}));
}
// ISSUE: reference to a compiler-generated field
Action<CallSite, object, object> action = Program.u003CMainu003Eo__SiteContainer0.u003Cu003Ep__Site1.Target;
// ISSUE: reference to a compiler-generated field
CallSite<Action<CallSite, object, object>> callSite = Program.u003CMainu003Eo__SiteContainer0.u003Cu003Ep__Site1;
object obj1 = instance;
// ISSUE: reference to a compiler-generated field
if (Program.u003CMainu003Eo__SiteContainer0.u003Cu003Ep__Site2 == null)
{
// ISSUE: reference to a compiler-generated field
Program.u003CMainu003Eo__SiteContainer0.u003Cu003Ep__Site2 = CallSite<Func<CallSite, object, object>>.Create(Binder.InvokeMember(CSharpBinderFlags.None, "GetCollectionName", (IEnumerable<Type>) null, typeof (Program), (IEnumerable<CSharpArgumentInfo>) new CSharpArgumentInfo[1]
{
CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, (string) null)
}));
}
// ISSUE: reference to a compiler-generated field
// ISSUE: reference to a compiler-generated field
object obj2 = Program.u003CMainu003Eo__SiteContainer0.u003Cu003Ep__Site2.Target((CallSite) Program.u003CMainu003Eo__SiteContainer0.u003Cu003Ep__Site2, instance);
action((CallSite) callSite, obj1, obj2);
// ISSUE: reference to a compiler-generated field
if (Program.u003CMainu003Eo__SiteContainer0.u003Cu003Ep__Site3 == null)
{
// ISSUE: reference to a compiler-generated field
Program.u003CMainu003Eo__SiteContainer0.u003Cu003Ep__Site3 = CallSite<Action<CallSite, Type, object>>.Create(Binder.InvokeMember(CSharpBinderFlags.ResultDiscarded, "Print", (IEnumerable<Type>) null, typeof (Program), (IEnumerable<CSharpArgumentInfo>) new CSharpArgumentInfo[2]
{
CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.UseCompileTimeType | CSharpArgumentInfoFlags.IsStaticType, (string) null),
CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, (string) null)
}));
}
// ISSUE: reference to a compiler-generated field
// ISSUE: reference to a compiler-generated field
Program.u003CMainu003Eo__SiteContainer0.u003Cu003Ep__Site3.Target((CallSite) Program.u003CMainu003Eo__SiteContainer0.u003Cu003Ep__Site3, typeof (Program), instance);
Console.ReadKey(true);
}
请注意类型和激活是如何合并到一行中的。方法主体的其余部分(除了底部的 Print)用于调用两个嵌套方法,这两个方法看起来像是已扩展为两个调用。
不,方法不是简单地使用该名称查找的。(dynamic
不仅限于方法;它还可用于绑定到字段/属性。如果相关类扩展DynamicObject
,则实际上可以操作绑定表达式。举个简单的例子:
class DynamicDictionary : DynamicObject {
private Dictionary<string, object> Dict = /*...*/;
public override bool TryGetMember(GetMemberBinder binder, out object result) {
result = Dict[binder.Name];
return true;
}
}
现在,如果您运行以下代码:
dynamic dict = new DynamicDictionary();
object o = dict.Abc123;
DynamicDictionary
,而不是尝试获取字段或属性dict.Abc123
,而是尝试查找并返回Dict["Abc123"]
。
编辑:在这里找到了一篇关于dynamic
如何工作的更深入的文章。