在动态对象上调用方法基本上是 GetMethod() 吗?调用()



标题基本上是我的问题。在封面下是最后一行:

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如何工作的更深入的文章。

最新更新