Ayende @ Rahien

Refunds available at head office

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

João P. Bragança
01/04/2011 04:50 PM by
João P. Bragança

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.

Søren Trudsø Mahon
01/04/2011 04:56 PM by
Søren Trudsø Mahon

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

João P. Bragança
01/04/2011 05:55 PM by
João P. Bragança

But what is the reason to have anonymous types be internal to the assembly?

Dmitry
01/04/2011 06:13 PM by
Dmitry

Anonymous types are internal to assemblies. This is by CLR design.

One solution is to use ExpandoObject instead.

Hendry Luk
01/05/2011 01:12 AM by
Hendry Luk

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

{

public static object GetChild()

{

    return new ChildObject();

}

// ==!! note this private class !!==

private class ChildObject

{

    public int Key {get{return 10;}}

}

}

// ... ===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.

configurator
01/05/2011 04:39 AM by
configurator

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.

Paulo Morgado
01/05/2011 03:09 PM by
Paulo Morgado

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?

João P. Bragança
01/05/2011 05:33 PM by
João P. Bragança

I get that it is by CLR design, I am asking WHY anonymous types should be internal and not public.

J Healy
01/06/2011 02:30 AM by
J Healy

It's a 'feature', not a bug.

Hendry Luk
01/06/2011 03:51 AM by
Hendry Luk

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.

Comments have been closed on this topic.