Click here to Skip to main content
Click here to Skip to main content

DeepObject: A Multi-level C# 4.0 Dynamic Object

, 15 Oct 2012 CPOL
Rate this:
Please Sign up or sign in to vote.
A class to create multi-level dynamic objects in C# 4.0

Introduction

So, we all know now that C# came with the capability of having dynamic objects. We all know it came also with the nice ExpandoObjetc class that permits you to use those capabilities in a very easy and straightforward way. But what happens when you want to use a multi-level dynamic object?

Let me elaborate it a bit more. Simply speaking, the normal objects you use regularly are non-dynamic objects, meaning that the compiler uses the early-binding behavior by which it checks that the object's properties and methods exist at compile time. So, with them, if you use a property or a method that don’t exist in the class definition, your code won’t compile.

But when you use the new dynamic keyword to declare an object, you are instructing the compiler to use a late-binding mechanism, by which all those checks are postponed until run-time. In other words, when using a dynamic "type", the compiler understands that the actual type of the object is not going to be known until run-time, and that only then it is valid to perform the actual binding.

The nicest thing is that when the actual object used inherits from DynamicObject, or if it implements the IDynamicMetaObjectProvider interface, then you are allowed to intercept this late-binding mechanism and tell back the DLR (the dynamic analogous of the CLR) the way you want to deal when accessing those properties, or invoking those methods, that didn't exist in the class definition. For instance, a popular way is by by accessing a dictionary where to store the values of the properties along with their names, etc. (actually, this is pretty much what the ExpandoObject does).

Now, what if you want one of those dynamic properties to be a dynamic one itself? I mean one that is able to have its own set of second-level dynamic properties? So what happens if you want to use the following code?:

dynamic employee = new ...;
employee.Id = "007";
employee.Name.FirstName = "James";
employee.Name.FamilyName = "Bond";

The above scenario is not supported by any object I was aware of. On top of that, I wanted such an object to be serializable (so serializing whatever arbitrary contents it may hold), and disposable (so that I can guarantee no references to any object are kept if I invoke its Dispose() method).

There is no limit in the depth of the dynamic structure you are creating. And, of course, no restrictions at all to the type of values you can set into any of those dynamically created properties. Finally, you can also use indexes to create and access some properties, as we can see in the next example:

employee.Address[0] = "MI-5";
employee.Address[1] = "London";
employee.Address[2] = "United Kingdom";

You can use as many indexes, and of whatever types, as you need. Yes, a given index can have a null value as well. The only restriction is that the indexes list cannot be empty (which, by the way, mimics the restriction the C# compiler imposes).

DeepObject’s basics

DeepObject inherits from DynamicObject, and overrides its TryGetXXX() methods to permit the interception of the dynamic bindings. Internally it maintains a list of members where the new dynamic properties are stored, each of them being its own instance of DeepObject.

And this is the key fact: in this recursive way each of them are able to store either contents, to use them as regular dynamic properties to get or set these contents, or storing its own set of second-level dynamic properties. The TryGetXXX() methods are overridden in such a way that deal with all the complexities on your behalf.

DeepObject instances have just one major property: Deep, of type DeepCarrier. Its mission is to store all the internal details without polluting your dynamic instance with methods and properties, and by using it you can get access to a more advanced manipulation of your DeepObject instances. Deep has itself the following set of properties and methods:

  • The Members property permits the enumeration over the list of dynamic members. As expected, the Count property returns the number of members the instance has – note that this count only reflects its direct members, not the nth-level members it may conceptually have.
  • The idea is to let you use the dynamic syntax capabilities, as in the example, to automatically add the members you will need. But, if for whatever reason you need to add manually a member, instead of performing a dynamic binding you can do so by using any of the AddMember() methods. You can also remove a member using the RemoveMember() method, or clear all members by using the Clear() method.
  • Sometimes is useful to refer to a given member by the index in which it is stored in the internal list. To obtain this index you can use the IndexOf() method.
  • Its Name property returns the name used for this instance when it was dynamically created. Note also that you can give a name to these instances when you create them by using their constructors, something I found useful in some circumstances. If a given instance is hosted into another one, its FullName property returns the concatenation with dots of the full name of its parent plus its own given name, in a recursive way.
  • Note that when this member is an indexed one, its name is assigned automatically by using a deterministic algorithm (that basically concatenates the values of the indexes used to instantiate this member).
  • It is obvious that two members cannot share the same name, and the default behavior is to perform this comparison using a case-sensitive comparison. But you can set the mode to be case-insensitive by using the appropriate parameter in the constructor. In this case, this setting is propagated also to any child member is automatically created by the dynamic binding mechanism. You can get this setting by using the CaseSensitiveNames property.
  • Also, it may happen that at a given moment in time you may want to block the object’s structure, so not allowing to add any more members or remove any of them. You can get or set this behavior by using the Sealed property.
  • You can also get what value this member store, by using its Value property. If no value has been assigned to it, it returns null by default. You can find out if a value has been assigned to it or not in its HasValue property. Also, you can clean the value (meaning that HasValue will return false again) by using the ResetValue() method.
  • IsIndex will return true if the member is an indexed one, or false otherwise. An indexed member is one that requires an index, or several ones, to access it.
  • Host will return the hosting instance of this one as a member, or null if it has no parent (it is a standalone instance and not a member of anyone).
  • Level will return the level of this object: cero if it is not a member of anyone, 1 if it is a first-level member, 2 if it is a second-level member, and so on.

Changes

  • In this version I have dropped support for dynamic multi-level methods, because I have never end up using them. But if this causes a major problem, it won’t be hard to include them again, just let me know.

History

  • [v3, August 2012]: cleaner architecture and performance improvements. Dropped support for dynamic methods.
  • [v2, October 2010]: new methods and features added.
  • [v1, May 2010]: initial version.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Moises Barba

Spain Spain
Moises Barba has worked as CIO and CTO for some start-ups, and as Consulting Director for some major multinational IT companies. Solving complex puzzles and getting out of them business value has ever been among his main interests - that's why he has spent his latest 20 years trying to combine his degree in Theoretical Physics and his System Engineer MCSE with his MBA... and he is still trying to figure out how all these things can fit together. Even if flying a lot across many countries, along with the long working days that are customary in IT management and Consultancy, he can say that, after all, he lives in Spain (at least the weekends).
Follow on   Twitter   LinkedIn

Comments and Discussions

 
QuestionCasting to other objects. PinmemberMember 1016216216-Jan-14 14:46 
AnswerRe: Casting to other objects. PinmemberMoises Barba16-Jan-14 22:08 
GeneralMy vote of 5 Pinmemberfredatcodeproject19-Aug-13 4:48 
GeneralRe: My vote of 5 PinmemberMoises Barba21-Sep-13 11:20 
QuestionHow to serialize to Json? PinmemberMember 469702022-Oct-12 17:36 
AnswerRe: How to serialize to Json? PinmemberMoises Barba23-Oct-12 0:04 
SuggestionRe: How to serialize to Json? PinmemberMember 469702023-Oct-12 17:29 
QuestionMy vote of 5 PinmemberAnkush Bansal15-Oct-12 0:59 
AnswerRe: My vote of 5 PinmemberMoises Barba15-Oct-12 6:02 
GeneralMy vote of 4 Pinmemberbartolo3-Sep-12 6:25 
GeneralRe: My vote of 4 PinmemberMoises Barba25-Sep-12 21:48 
GeneralMy vote of 5 Pinmembervisalia2-Sep-12 9:04 
GeneralRe: My vote of 5 PinmemberMoises Barba25-Sep-12 21:48 
GeneralMy vote of 5 Pinmembermbsmbs30-Aug-12 6:01 
GeneralRe: My vote of 5 PinmemberMoises Barba30-Aug-12 8:07 
AnswerReally a bit basic and out of date PinmemberClifford Nelson29-Aug-12 14:47 
GeneralRe: Really a bit basic PinmemberMoises Barba29-Aug-12 14:50 
GeneralRe: Really a bit basic PinmemberClifford Nelson30-Aug-12 7:27 
Generalhiiiiiiiiii Pingroupglory44415-Apr-11 9:58 
GeneralAmazing Pinmemberjapp2710-Dec-10 8:45 
GeneralRe: Amazing Pinmembermbarbac10-Dec-10 22:19 
GeneralRe: Amazing Pinmemberwonderdelight18-Jan-11 4:36 
Generalerror PinmemberJohn Giblin15-Nov-10 18:36 
GeneralRe: error Pinmembermbarbac17-Nov-10 2:40 
GeneralRe: error PinmemberJohn Giblin18-Nov-10 13:32 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web02 | 2.8.141220.1 | Last Updated 15 Oct 2012
Article Copyright 2010 by Moises Barba
Everything else Copyright © CodeProject, 1999-2014
Layout: fixed | fluid