List<dynamic> elements have fields but I cannot access them. Why?

本小妞迷上赌 提交于 2019-12-04 19:33:52


I need to loop over a List<dynamic> objects.

The list's objects all have values, but for some reason, I am not able to access any of the dynamic object fields. Below is a screenshot of my debug window:

enter image description here

There you can see the object contains fields (such Alias, Id, Name, etc).

I tried both casting it to a IDictionary<string, object> and ExpandoObject, to no avail. I did not face such a thing before: failing to access existing fields in a dynamic object when they exist.

What is wrong here?

The code is throwing a Microsoft.CSharp.RuntimeBinder.RuntimeBinderException with a message stating {"'object' does not contain a definition for 'Name'"}.

The list was created adding anonymously-typed objects, like this:

return new List<dynamic>(fields.Select(field => new 
                            Id = field.Id, 
                            Alias = field.Alias, 
                            Name = field.Name, 
                            Type = field.Type, 
                            Value = field.Value,
                            SortOrder = field.SortOrder

where fields is an ICollection<Field>, a strongly-typed collection.


The telling part is the exception:

{"'object' does not contain a definition for 'Name'"}.

This indicates that the runtime binder was not actually capable of accessing the type you're passing in dynamic (since dynamic does actually enforce visibility rules).

The most likely cause of this is that you're creating the anonymous type in a different assembly from the one where you're subsequently reading it - since anonymous types are declared internal, the consuming assembly cannot access it, causing the error message above.

Contrast with the usual case of runtime binder exceptions:

'<>f__AnonymousType0< string >' does not contain a definition for 'Name'


A possible solution to the problem is to use the InternalsVisibleToAttribute on the assembly containing the anonymous type. However, this is code smell - just like any other use of InternalsVisibleToAttribute or internal itself.

A better way would be to make sure you don't actually pass anonymous types over assembly boundaries - after all, they shouldn't even be used outside of the method they originated from; the fact that they are is basically an implementation detail of .NET - they didn't have another way to do the same thing. This could change in future versions, making the InternalsVisibleToAttribute solution doubly unreliable.

The way your code is using dynamic suggests that your team has flawed assumptions about how dynamic works and how it's supposed to be used. Note how the actual runtime type of List<dynamic> is actually List<object>. The same goes for arguments of type dynamic (which are again just object, albeit marked with DynamicAttribute). And in fact, that really is what dynamic is - it's a way to handle runtime dynamic dispatch - it's not a property of the type or anything, it's just the way you actually invoke whatever you're trying to invoke. For C#, dynamic allows you to skip most of the compiler checks when working with those dynamic types, and it generates some code to handle the dispatch for you automatically, but all of that only happens inside the method where you actually use the dynamic keyword - if you used List<object>, the end result would be exactly the same.

In your code, there's no reason not to use simple static types. Dynamic typing doesn't really give you any benefits, apart from the effort to code the types themselves. If your co-workers don't like that, well, they should present a better solution - the problem is quite obvious, and it's something you need to deal with.

Much worse, it explicitly hides all context, all the type information. That's not something you want in an API, internal or not! If you want to hide the concrete types being used, why not - but you should still expose an interface instead. I suspect this is the reason why anonymous types can't implement interfaces - it would encourage you to go entirely the wrong way.