1

How do I make the complex structure of an anonymous object public inside a dynamic object?

Anonymous objects are flagged as internal, so I'm looking for a creative way to work around this.

// This is the library I control
public void SendObject() {
    var anonymous = new {
        Text = "Test",
        SubItem = new {
            SubText = "Bla",
            SubSub = new {
                SubSubText = "Baha"
            }
        }
    };
}

dynamic dyn = ExposeAnonymous(anonymous); // Perform voodoo

var result = ExternalLibrary.GetSpecialProperty(dyn);

// External library I don't control
public object GetSpecialProperty(dynamic dyn) {
    return dyn.SubItem.SubSub.SubSubText;
}

The problem is when sending the dynamic to other external libraries, that I don't control, you get an error like:

'object' does not contain a definition for 'SubItem'.

Seb Nilsson
  • 26,200
  • 30
  • 103
  • 130

3 Answers3

6

The problem is when sending the dynamic to other libraries,

... And there's the rub. Anonymous types are declared as internal by the C# compiler, which means that other assemblies don't have access to them.

Either stop using anonymous types, or use [InternalsVisibleToAttribute] to make the type visible to other assemblies. Within the assembly containing the type which creates the instance of the anonymous type, use:

[InternalsVisibleTo("ExternalLibrary")]

(I'd actually expect the issue to be on SubItem rather than SubSub...)

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • It's a (buggy?) library I can't control, so `InternalsVisibleToAttribute` won't work. – Seb Nilsson Apr 23 '13 at 15:39
  • @SebNilsson: *What's* a library you can't control? The one returning the object, or the one consuming it? If you can't change the one returning the object, you may need to resort to reflection. (Even that won't work if you're not trusted appropriately.) – Jon Skeet Apr 23 '13 at 15:43
  • I think maybe this question was too out of context and I was hoping to get a quickfix. It's basically the RazorEngine-library that has problems with dynamic from anonymous types. But "stop using anonymous" for many, many different data-structures is not an option. – Seb Nilsson Apr 23 '13 at 15:45
  • @SebNilsson: So which library *is* in your control? You're still not being very clear, to be honest. – Jon Skeet Apr 23 '13 at 15:47
  • The code sending in the model as an anonymous object into RazorEngine. – Seb Nilsson Apr 23 '13 at 15:51
  • 2
    @SebNilsson: Right, so why can't you add `InternalsVisibleTo` so that your types are visible to RazorEngine? In fact, is it *really* RazorEngine which is using it, or is it some code which you're defining, but which is *used* by RazorEngine? You should edit your question to give a *lot* more detail. – Jon Skeet Apr 23 '13 at 15:54
  • I was trying to make the question more general, to maybe find some creative solutions, like Serialize/Deserialize and actually learning more about the innards of dynamics in .NET. – Seb Nilsson Apr 23 '13 at 15:56
  • @SebNilsson: It's hard to propose "creative solutions" without knowing the constraints though. I proposed a solution, and you rejected it for a reason which I couldn't possibly have known about from the question. What's to stop exactly the same thing happening with the next solution? – Jon Skeet Apr 23 '13 at 16:33
  • I've cleaned up the question a bit. But "stop using anonymous" is the classic SO-problem with the question "how do I do X **without** jQuery?" -- "Just use jQuery". It's not really constructive. And the question is not about the fact that anonymous are internal, but creative ways to work around it. – Seb Nilsson Apr 24 '13 at 06:15
  • @SebNilsson: Well the question didn't give any indication that you even knew the reason for the failure. If you did, it would have been really helpful to have said so. And my answer *did* show a way round it in the common case where `InternalsVisibleTo` is appropriate. Indeed, there's still nothing in the question which explains why you can't use that. – Jon Skeet Apr 24 '13 at 06:27
  • "Still"? How many times do I have to write "external library that I don't control"? Help me out here, I want to understand for the next time I write a question. What was unclear about the initial question "How do I make the complex structure of an anonymous object public inside a dynamic object?"? It does say "how", not "why is this happening". – Seb Nilsson Apr 24 '13 at 06:31
  • You don't need to control the external library to give internal access to it. You need to control the library which wants to *provide* the access. So if assembly A passes assembly B an anonymous type that assembly B wants to use with `dynamic`, it's only assembly A which needs to be changed to say `[InternalsVisibleTo(B)]`. So yes, there's still nothing in the question prohibiting that. As for what was unclear about the original question: it was unclear that you understood what the problem was to start with, so I took the time to explain it. If you already knew the reason, my time was wasted. – Jon Skeet Apr 24 '13 at 06:36
  • (You also wasted the time of anyone who was looking for *other* reasons why the dynamic part wasn't working.) – Jon Skeet Apr 24 '13 at 06:36
  • Ok, points taken. Clarify that you don't need to control the external library to use `InternalsVisibleTo` and it's probably the correct answer. – Seb Nilsson Apr 24 '13 at 06:40
  • @SebNilsson: Done - although if you'd looked into `InternalsVisibleTo` instead of assuming it wasn't a valid approach, I think this would have become obvious very quickly. – Jon Skeet Apr 24 '13 at 06:43
  • Like I said, I wanted a more broad answer than to this specific problem. I'll try `InternalsVisibleTo`, but it felt not clean, and see if it works. – Seb Nilsson Apr 24 '13 at 06:49
  • It didn't work for this specific scenario, but probably is the correct answer in general. I'll post a specific question about RazorEngine, with code, stacktraces and a check-list of knowns. – Seb Nilsson Apr 24 '13 at 06:55
  • (Making it a general question probably increases the chance of getting answers from the Jon Skeets in the world, rather than a RazorEngine-specific one... Sorry, bad choice this time...) – Seb Nilsson Apr 24 '13 at 06:59
5

Anonymous types are internal and the DLR does the same accessibility analysis at run-time that the compiler does at compile-time. So you cannot access the anonymous type's members from another assembly using dynamic.

One option might be to use ExpandoObject:

        dynamic a = new ExpandoObject();
        a.Text = "Test";
        a.SubItem = new ExpandoObject();
        a.SubItem.SubText = "Blah";
        a.SubItem.SubSub = new ExpandoObject();
        a.SubItem.SubSub.Text = "Baha";

This is kind of ugly so, you could keep the anonymous type and use a helper method to recursively convert to an ExpandoObject:

    public static dynamic ConvertToExpando(object obj)
    {
        IDictionary<string, object> expando = new ExpandoObject();
            foreach(var pi in obj.GetType().GetProperties())
            {
                // there doesn't seem to be a way to know if it is an anonymous type directly. So I use IsPublic here.
                if (pi.PropertyType.IsPublic)
                {
                    expando[pi.Name] = pi.GetValue(obj);                    
                }
                else
                {
                    expando[pi.Name] = ConvertToExpando(pi.GetValue(obj));
                }
            }
        return expando;
    }

This does the "Perform voodoo" that you need.

Mike Zboray
  • 39,828
  • 3
  • 90
  • 122
  • It should be possible to write something to use reflection to convert an anonymous type into an ExpandoObject recursively, too... – Jon Skeet Apr 23 '13 at 16:34
  • This was one of the solutions I looked at and feared of receiving. I know this works, but it looks awful... – Seb Nilsson Apr 24 '13 at 06:17
  • 1
    @SebNilsson You are tough to please. It has to work and be pretty, huh. I added a helper method that recursively converts the anonymous type to `ExpandoObject` (as suggested by Jon Skeet). – Mike Zboray Apr 24 '13 at 16:31
  • @JonSkeet Good idea, it is actually pretty straightforward so I added it to my answer. – Mike Zboray Apr 24 '13 at 16:32
  • @mikez I like the creative answer. But is `IsPublic` the correct qualifier or does it just happen to work in this case? :) – Seb Nilsson Apr 25 '13 at 06:40
  • @SebNilsson I don't think there is a way to identify an anonymous type directly. But the issue is that the type you are passing is not public. You would see the same error if you had used an internal or private nested type that you explicitly declared. – Mike Zboray Apr 26 '13 at 07:01
  • @mikez There seems to be a hacky way to detect anonymous types actually: http://stackoverflow.com/questions/2483023/how-to-test-if-a-type-is-anonymous But I get your point. – Seb Nilsson Apr 26 '13 at 07:06
1

With my opensource framework ImpromptuInterface (in nuget) you can create complex expando graphs with an inline syntax.

dynamic New = Builder.New<ExpandoObject>();

dynamic dyn = New.Obj(
        Text: "Test",
        SubItem: New.Obj(
            SubText: "Bla",
            SubSub: New.Obj(
                SubSubText: "Baha"
            )
        )
    );
jbtule
  • 31,383
  • 12
  • 95
  • 128