Ayende @ Rahien

Hi!
My name is Ayende Rahien
Founder of Hibernating Rhinos LTD and RavenDB.
You can reach me by phone or email:

ayende@ayende.com

@

Posts: 5,949 | Comments: 44,544

filter by tags archive

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

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

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

Dmitry

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

One solution is to use ExpandoObject instead.

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

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

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

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

J Healy

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

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.

Comment preview

Comments have been closed on this topic.

FUTURE POSTS

  1. The RavenDB Comic Strip: Part III – High availability & sleeping soundly - one day from now

There are posts all the way to May 28, 2015

RECENT SERIES

  1. The RavenDB Comic Strip (3):
    20 May 2015 - Part II – a team in trouble!
  2. Special Offer (2):
    27 May 2015 - 29% discount for all our products
  3. RavenDB Sharding (3):
    22 May 2015 - Adding a new shard to an existing cluster, splitting the shard
  4. Challenge (45):
    28 Apr 2015 - What is the meaning of this change?
  5. Interview question (2):
    30 Mar 2015 - fix the index
View all series

RECENT COMMENTS

Syndication

Main feed Feed Stats
Comments feed   Comments Feed Stats