summaryrefslogtreecommitdiff
path: root/external/aspnetwebstack/src/System.Web.WebPages/ReflectionDynamicObject.cs
blob: c2cce6f36e41ce99bce3b8adcaf0e8337f46c40e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
// 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();
        }
    }
}