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.
source to share
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.
source to share
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.
source to share
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 .
source to share