Whether the method calls the method of the dynamic object basically GetMethod (). Invoke ()?

The title is basically my question. Under the cover makes the last line:

Type xmlCodecType = typeof(XmlCodec<>).MakeGenericType(typeof(SomeObjectProperty));
dynamic xmlCodec = Activator.CreateInstance(xmlCodecType);
xmlCodec.ReadCollection(xmlCodec.GetCollectionName());

      

basically do this:

MethodInfo method1 = xmlCodec.GetType().GetMethod("ReadCollection");
MethodInfo method2 = xmlCodec.GetType().GetMethod("GetCollectionName");
method1.Invoke(xmlCodec, new obj[] { method2.Invoke(xmlCodec, null) });

      

by doing

Most of the time I use the reflection method because that's what I'm used to and it just feels a little more "compile-time error traps" with passing write types and objects. Dynamics is a little more hands. However, reflection can be more difficult to read / follow at times, whereas dynamics is not always a key word in a language, this is a common concept for most of them.

+3


source to share


3 answers


Here's a simple bit of test code:

void Main()
{
    this.DoSomething();
}

private void DoSomething()
{
    Console.WriteLine("Foo");
}

      

This compiles for this 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         

      

Now if I enter a link dynamic

:

void Main()
{
    dynamic bar = this;
    bar.DoSomething();
}

private void DoSomething()
{
    Console.WriteLine("Foo");
}

      



Here's the 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         

      

And this decompiles:

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;
}

      

That is, as far as I can tell, exactly what is called.

+1


source


To add to the other answers, I did the following test (more specifically, how you structure your code):

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);
    }

}

      

Now, in DotPeek, it looks like this (printing method omitted for brevity):



private static void Main(string[] args)
{
  object instance = Activator.CreateInstance(typeof (XmlCodec<>).MakeGenericType(typeof (string)));
  // ISSUE: reference to a compiler-generated field
  if (Program.\u003CMain\u003Eo__SiteContainer0.\u003C\u003Ep__Site1 == null)
  {
    // ISSUE: reference to a compiler-generated field
    Program.\u003CMain\u003Eo__SiteContainer0.\u003C\u003Ep__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.\u003CMain\u003Eo__SiteContainer0.\u003C\u003Ep__Site1.Target;
  // ISSUE: reference to a compiler-generated field
  CallSite<Action<CallSite, object, object>> callSite = Program.\u003CMain\u003Eo__SiteContainer0.\u003C\u003Ep__Site1;
  object obj1 = instance;
  // ISSUE: reference to a compiler-generated field
  if (Program.\u003CMain\u003Eo__SiteContainer0.\u003C\u003Ep__Site2 == null)
  {
    // ISSUE: reference to a compiler-generated field
    Program.\u003CMain\u003Eo__SiteContainer0.\u003C\u003Ep__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.\u003CMain\u003Eo__SiteContainer0.\u003C\u003Ep__Site2.Target((CallSite) Program.\u003CMain\u003Eo__SiteContainer0.\u003C\u003Ep__Site2, instance);
  action((CallSite) callSite, obj1, obj2);
  // ISSUE: reference to a compiler-generated field
  if (Program.\u003CMain\u003Eo__SiteContainer0.\u003C\u003Ep__Site3 == null)
  {
    // ISSUE: reference to a compiler-generated field
    Program.\u003CMain\u003Eo__SiteContainer0.\u003C\u003Ep__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.\u003CMain\u003Eo__SiteContainer0.\u003C\u003Ep__Site3.Target((CallSite) Program.\u003CMain\u003Eo__SiteContainer0.\u003C\u003Ep__Site3, typeof (Program), instance);
  Console.ReadKey(true);
}

      

Notice how type and activation have been combined into one line. The rest of the method body (except Print Bottom) is for calling two nested methods that look like they've been extended by two calls.

+1


source


No, the method doesn't just look for that name. ( dynamic

not limited to just methods, although it can also be used to bind to fields / properties.) In fact, binding expressions can be manipulated if the class in question extends DynamicObject

. As a simple example:

class DynamicDictionary : DynamicObject {
    private Dictionary<string, object> Dict = /*...*/;

    public override bool TryGetMember(GetMemberBinder binder, out object result) {
        result = Dict[binder.Name];

        return true;
    }
}

      

Now if you run the following code:

dynamic dict = new DynamicDictionary();
object o = dict.Abc123;

      

DynamicDictionary

instead of trying to get a field or property dict.Abc123

, it will try to search and return Dict["Abc123"]

.

EDIT: Found a more detailed post on how it dynamic

works here .

0


source







All Articles