The BCL bug of the day
Now this one if quite an interesting one. Let us take a look and see what happen when we have the following calling code:
public class Program { static void Main() { dynamic d = new MyDynamicObject(); Console.WriteLine(d.Item.Key); } }
And the following MyDynamicObject:
public class MyDynamicObject : DynamicObject { public override bool TryGetMember(GetMemberBinder binder, out object result) { result = new {Key = 1}; return true; } }
What do you expect the result of executing this code would be?
If you think that this will print 1 on the console, you are absolutely correct.
Except…
If Program and MyDynamicObject are on separate assemblies.
In that case, we end up with a terribly confusing message:
Microsoft.CSharp.RuntimeBinder.RuntimeBinderException was unhandled Message='object' does not contain a definition for 'Key' Source=Anonymously Hosted DynamicMethods Assembly StackTrace: at CallSite.Target(Closure , CallSite , Object ) at System.Dynamic.UpdateDelegates.UpdateAndExecute1[T0,TRet](CallSite site, T0 arg0) at ConsoleApplication1.Program.Main() at System.AppDomain._nExecuteAssembly(RuntimeAssembly assembly, String[] args)
I have been able to narrow this down to “anonymous objects from a different assembly”.
Now that you have the bug, figure out:
- Why this is happening?
- How would you work around this bug?
- How would you reproduce this bug without using anonymous types?
- How would you fix this bug?
- What should you be careful when fixing this bug?
- What would be Microsoft’s response to that?
Comments
The "bug" is well described here stackoverflow.com/.../return-consume-dynamic-an...
Isn't this because the members of anonymous types are internal? I've run into this same issue trying to use ViewPage[of dynamic] with anonymous models.
There is some sample code floating on the net that let's you use anonymous types with dynamic.
Aren't anonymous types internal?
Make a public class with public property key?
So to reproduce make a class with private or internal getter?
MS, would properly say it's by design;)
But what is the reason to have anonymous types be internal to the assembly?
Anonymous types are internal to assemblies. This is by CLR design.
One solution is to use ExpandoObject instead.
By default DLR uses the standard statically compiled runtime, not reflection. So you can only access stuff that you can normally access statically.
So your exception is caused by the same reason you can't do this:
public static class SomeClass
{
// ==!! note this private class !!==
}
// ... ===Test===:
dynamic obj = SomeClass.GetChild();
Assert.AreEqual(obj.Key, 10); //--> FAIL!
Same assembly or otherwise, that code will throw the same exception.That's because at runtime, the DLR casts our obj variable as type "Object" (instead of "ChildObject"), and obviously Object doesnt have "Key" property.
If you change the type of ChildObject as public, then the test will pass, because now the DLR will cast your obj variable as "ChildObject" type, and it has a "Key" property.
That's because DLR is using strong-type execution at runtime, not reflection. So if you want to access non-accessible members, you have to route your dynamic to use reflection, e.g. by using an IDynamicMetaObjectProvider implementation that calls reflection.
So yea, I would say it's by design.
Like everyone else said, this is by design and is not a bug; you can't access internal fields or properties through a different assembly by using dynamic, as it uses the current execution context due to the principle of least surprise.
Your point of view makes sense if look at MyDynamicObject as a special case of DynamicObject but nothing more than a DynamicObject.
On the other hand, if you look at MyDynamicObject deriving from DynamicObject as an implementation detail, the way it works by design makes more sense.
Have you tried to cast to DynamicObject?
I get that it is by CLR design, I am asking WHY anonymous types should be internal and not public.
It's a 'feature', not a bug.
Joao, that's a valid point. But mind you that anonymous types predate dynamics, and there wasnt any point making it public at that time. It would only make it harder for the compiler to generate the unique names. The compiler would then have to search through all referenced assemblies.
Anonymous types are internal. I wrote about this a few moons ago:
www.heartysoft.com/.../...s-c-sharp-4-dynamic.aspx
If you set internalsvisibleto for the declaring assembly, it will work as expected. It's more of a nuisance than a bug.
Comment preview