Click here to Skip to main content
15,896,118 members
Articles / Programming Languages / C# 4.0

DeepObject: A Multi-level C# 4.0 Dynamic Object

Rate me:
Please Sign up or sign in to vote.
4.90/5 (30 votes)
15 Oct 2012CPOL6 min read 95K   1.5K   102  
A class to create multi-level dynamic objects in C# 4.0
// ==============================================
// #define UNDER_CONSTRUCTION
namespace MBLib.SystemTools.Dynamic
{
	using System;
	using System.Text;
	using System.Collections.Generic;
	using System.Dynamic;
	using System.Diagnostics;

	// ===========================================
	public class DeepObject : DynamicObject, ICloneable
	{
		public DeepObject( string name = null, bool caseSensitive = true )
		{
			__name = name == null ? null : name.Validated( "DeepObject Name", invalidChars: TypeHelper.InvalidMemberNameChars );
			__caseSensitive = caseSensitive;
		}
		#region
		private string __name = null;
		private bool __isIndex = false;
		private bool __caseSensitive = true;
		private DeepObject __parent = null;
		private List<DeepObject> __members = null;
		private object __value = null;
		private bool __hasValue = false;
		private bool __cloneOnCopying = true;
		#endregion

		public string DeepName()
		{
			return __name;
		}
		public void DeepSetName( string name )
		{
			if( name == null ) {
				if( __parent == null ) { __name = null; return; }
				throw new InvalidOperationException( "Name cannot be cleaned in DeepObjects that are members of a tree." );
			}
			if( __parent != null ) {
				if( __parent.DeepMember( name, raise: false ) != null )
					throw new InvalidOperationException( "Cannot change name because it already exists as a member in the parent tree." );
			}
			__name = name;
		}

		public bool DeepCaseSensitive()
		{
			return __caseSensitive;
		}
		public bool DeepIsIndex()
		{
			return __isIndex;
		}

		public object DeepValue()
		{
			return __value;
		}
		public bool DeepHasValue()
		{
			return __hasValue;
		}

		public void DeepSetValue( object value )
		{
			__hasValue = true;
			__value = value;
		}
		public void DeepClearValue()
		{
			__hasValue = false;
			__value = null;
		}
		public bool DeepIsWrapper()
		{
			return ( __hasValue && __members == null ) ? true : false;
		}

		public DeepObject DeepParent()
		{
			return __parent;
		}
		public int DeepLevel()
		{
			if( __parent == null ) return 0;
			return __parent.DeepLevel() + 1;
		}

		public bool DeepIsTree()
		{
			return __members == null ? false : true;
		}
		public int DeepCount()
		{
			return __members == null ? 0 : __members.Count;
		}
		public IEnumerable<DeepObject> DeepMembers()
		{
			return __members.AsReadOnly();
		}

		public DeepObject DeepMember( int n, bool raise = true )
		{
			if( __members == null ) {
				if( raise ) throw new InvalidOperationException( "This DeepObject is not a tree." );
				return null;
			}
			if( n < 0 || n >= __members.Count ) {
				if( raise ) throw new IndexOutOfRangeException( "Index (" + n + ") is invalid." );
				return null;
			}
			return __members[ n ];
		}
		public DeepObject DeepMember( string name, bool raise = true )
		{
			name = name.Validated( "Member name" );

			if( __members == null ) {
				if( raise ) throw new InvalidOperationException( "This DeepObject is not a tree." );
				return null;
			}

			StringComparison comparison = __caseSensitive ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase;
			DeepObject r = __members.Find( x => name.Equals( x.__name, comparison ) );
			if( r == null && raise ) throw new KeyNotFoundException( "Cannot find member: " + name );
			return r;
		}

		public DeepObject DeepAddMember( string name )
		{
			name = name.Validated( "Member name" );

			if( __members == null ) __members = new List<DeepObject>();
			if( DeepMember( name, raise: false ) != null ) throw new InvalidOperationException( "Member already exists: " + name );

			DeepObject r = new DeepObject( name, __caseSensitive ) { __parent = this }; __members.Add( r );
			return r;
		}
		public DeepObject DeepAddIndex( object[] indexes )
		{
			string name = DeepIndexesToString( indexes );

			if( __members == null ) __members = new List<DeepObject>();
			if( DeepMember( name, raise: false ) != null ) throw new InvalidOperationException( "Member already exists: " + name );

			DeepObject r = new DeepObject( null, __caseSensitive ) { __name = name, __parent = this }; __members.Add( r );
			r.__isIndex = true;
			return r;
		}
		public static string DeepIndexesToString( object[] indexes )
		{
			StringBuilder sb = new StringBuilder( "[" ); if( indexes != null ) {
				bool first = true; foreach( object index in indexes ) {
					if( !first ) sb.Append( "//" ); else first = false;
					sb.AppendFormat( "{0}", index == null ? "NULL" : index.ToString() );
				}
			}
			sb.Append( "]" ); return sb.ToString();
		}

		public DeepObject DeepRemoveMember( int n )
		{
			DeepObject r = DeepMember( n, raise: true );
			if( r != null ) { __members.Remove( r ); r.__parent = null; }
			return r;
		}
		public DeepObject DeepRemoveMember( string name )
		{
			DeepObject r = DeepMember( name, raise: true );
			if( r != null ) { __members.Remove( r ); r.__parent = null; }
			return r;
		}
		public void DeepClearMembers()
		{
			if( __members != null ) {
				foreach( DeepObject r in __members ) r.__parent = null;
				__members.Clear();
				__members = null;
			}
		}

		public string DeepToString()
		{
			StringBuilder sb = new StringBuilder();

			if( __name != null || __hasValue ) sb.Append( "[" );
			sb.AppendFormat( "{0}", __name );
			if( __hasValue ) sb.AppendFormat( ":{0}", __value == null ? "NULL" : __value.ToString() );

			if( __members != null ) {
				if( __name != null || __hasValue ) sb.Append( ", " );
				bool first = true; foreach( DeepObject member in __members ) {
					if( !first ) sb.Append( ", " ); else first = false;
					sb.Append( member.ToString() );
				}
			}

			if( __name != null || __hasValue ) sb.Append( "]" );
			return sb.ToString();
		}
		public override string ToString()
		{
			return DeepToString();
		}

		public bool DeepCloneOnCopying
		{
			get { return __cloneOnCopying; }
			set { __cloneOnCopying = value; }
		}
		public object Clone()
		{
			DeepObject cloned = new DeepObject( __name, __caseSensitive );

			// The deep value...
			if( __hasValue ) {
				if( __value != null ) {
					if( __value is ICloneable && __cloneOnCopying )
						cloned.__value = ( (ICloneable)__value ).Clone();
					else cloned.__value = __value;
				}
				cloned.__hasValue = true;
			}

			// The deep tree...
			if( __members != null ) foreach( DeepObject member in __members ) {
				member.__cloneOnCopying = __cloneOnCopying;
				DeepObject neo = (DeepObject)member.Clone();
				cloned.__members.Add( neo ); neo.__parent = cloned;
			}

			return cloned;
		}

		// ----------------------------------------
		#region
		[Conditional( "UNDER_CONSTRUCTION" )]
		static public void TraceBind( string format, params object[] args ) { Console.Write( format, args ); }
		#endregion
		public override IEnumerable<string> GetDynamicMemberNames()
		{
			List<string> list = new List<string>(); if( __members != null ) {
				foreach( DeepObject member in __members ) list.Add( member.__name );
			}
			return list;
		}

		public override bool TryGetMember( GetMemberBinder binder, out object result )
		{
			// If exists AND is a wrapper, return the value.
			// Otherwise, returns the member itself (assuring it is created ad-hoc if needed)
			TraceBind( "- GETMEMBER: {0} {1}\n", this.ToString(), binder.Name );

			DeepObject member = DeepMember( binder.Name, raise: false );

			if( member != null ) {
				if( member.DeepIsWrapper() ) result = member.DeepValue();
				else result = member;
			}
			else {
				member = DeepAddMember( binder.Name );
				result = member;
			}
			return true;
		}
		public override bool TrySetMember( SetMemberBinder binder, object value )
		{
			// Assign the value to the correct member (assuring it is created ad-hoc if needed).

			TraceBind( "- SETMEMBER: {0} {1} = {2}\n", this.ToString(), binder.Name, value == null ? "NULL" : value.ToString() );
			DeepObject member = DeepMember( binder.Name, raise: false );

			if( member == null ) member = DeepAddMember( binder.Name );
			member.DeepSetValue( value );

			return true;
		}

		public override bool TryGetIndex( GetIndexBinder binder, object[] indexes, out object result )
		{
			// If exists AND is a wrapper, return the value.
			// Otherwise, returns the member itself (assuring it is created ad-hoc if needed)
			// The member's name is composed by a deterministic internal function
			string name = DeepIndexesToString( indexes );
			TraceBind( "- GETINDEX: {0} {1}\n", this.ToString(), name );

			DeepObject member = DeepMember( name, raise: false );

			if( member != null ) {
				if( member.DeepIsWrapper() ) result = member.DeepValue();
				else result = member;
			}
			else {
				member = DeepAddIndex( indexes );
				result = member;
			}
			return true;
		}
		public override bool TrySetIndex( SetIndexBinder binder, object[] indexes, object value )
		{
			// Assign the value to the correct member (assuring it is created ad-hoc if needed).
			// The member's name is composed by a deterministic internal function
			string name = DeepIndexesToString( indexes );
			TraceBind( "- SETINDEX: {0} {1}\n", this.ToString(), name, value == null ? "NULL" : value.ToString() );

			DeepObject member = DeepMember( name, raise: false );

			if( member == null ) member = DeepAddIndex( indexes );
			member.DeepSetValue( value );

			return true;
		}

		public override bool TryConvert( ConvertBinder binder, out object result )
		{
			TraceBind( "- CONVERT: {0} to {1}\n", this.ToString(), binder.ReturnType );

			result = TypeHelper.ConvertTo( DeepIsWrapper() ? DeepValue() : this, binder.ReturnType );
			return true;
		}
		public override bool TryInvokeMember( InvokeMemberBinder binder, object[] args, out object result )
		{
			// If the member holds a delegate to a function, invoke it.
			TraceBind( "- INVOKEMEMBER: {0} {1}\n", this.ToString(), binder.Name );

			DeepObject member = DeepMember( binder.Name, raise: true );

			Delegate del = member.DeepValue() as Delegate;
			if( del == null ) throw new InvalidOperationException( "Member '" + binder.Name + "' is not an executable delegate." );

			// If the delegate's return type is 'void', returning null...
			if( del.GetType().Name.StartsWith( "Action" ) ) { del.DynamicInvoke( args ); result = null; }
			else result = del.DynamicInvoke( args );
			return true;
		}
	}
}
// ==============================================

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

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


Written By
Spain Spain
mbarbac has worked in start-ups, multinational tech companies, and consulting ones, serving as CIO, CTO, SW Development Director, and Consulting Director, among many other roles.

Solving complex puzzles and getting out of them business value has ever been among his main interests - and that's why he has spent his latest 25 years trying to combine his degree in Theoretical Physics 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).

Comments and Discussions