// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information. using System.Dynamic; using System.Globalization; using System.Reflection; namespace System.Web.WebPages { // Allows dynamic access over a CLR object via private reflection internal sealed class ReflectionDynamicObject : DynamicObject { private object RealObject { get; set; } public static object WrapObjectIfInternal(object o) { // If it's null, don't try to wrap it if (o == null) { return null; } // If it's public, leave it alone since the standard dynamic binder will work. Well, it won't work for // internal properties, but we're mostly concerned about supporting anonymous objects, which are never public if (o.GetType().IsPublic) { return o; } return new ReflectionDynamicObject() { RealObject = o }; } // Called when a property is accessed public override bool TryGetMember(GetMemberBinder binder, out object result) { // Get the property info PropertyInfo propInfo = RealObject.GetType().GetProperty( binder.Name, BindingFlags.GetProperty | BindingFlags.Instance | BindingFlags.Public); if (propInfo == null) { // If there is no such property, return null instead of failing. This allows optional parameters result = null; } else { // Get the property value result = propInfo.GetValue(RealObject, null); // Wrap the sub object if necessary. This allows nested anonymous objects to work. result = WrapObjectIfInternal(result); } return true; } // Called when a method is called public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result) { result = RealObject.GetType().InvokeMember( binder.Name, BindingFlags.InvokeMethod | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, RealObject, args, CultureInfo.InvariantCulture); return true; } // Called when the dynamic object needs to be converted to a non dynamic object public override bool TryConvert(ConvertBinder binder, out object result) { result = RealObject; return true; } public override string ToString() { // Just return the original object's display string return RealObject.ToString(); } } }