chr
2026-04-05 fe750b791d5b517cc4e9bc8e99a9a75139a0cfba
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
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
//            Copyright Keysight Technologies 2012-2019
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, you can obtain one at http://mozilla.org/MPL/2.0/.
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
 
namespace OpenTap
{
 
    /// <summary>
    /// Represents the members of C#/dotnet types.
    /// </summary>
    public class MemberData : IMemberData
    {
        
        struct MemberName
        {
            public string Name { get; set; }
            public Type DeclaringType { get; set; }
        }
        static ConcurrentDictionary<MemberName, MemberData> dict
            = new ConcurrentDictionary<MemberName, MemberData>();
        internal static void InvalidateCache()
        {
            dict.Clear();
        }
        /// <summary>
        /// Creates a new MemberData for a member of a C#/dotnet type.
        /// </summary>
        /// <param name="info"></param>
        /// <returns></returns>
        static public MemberData Create(MemberInfo info)
        {
            lock(dict)
                return dict.GetOrAdd(new MemberName { Name = info.Name, DeclaringType = info.DeclaringType }, x => new MemberData(x.Name, TypeData.FromType(x.DeclaringType)));
        }
 
        /// <summary> The System.Reflection.MemberInfo this represents. </summary>
        public readonly MemberInfo Member;
 
        private MemberData(string name, TypeData declaringType) : this(declaringType.Type.GetMember(name)[0], declaringType)
        { }
 
        private MemberData(MemberInfo info, TypeData declaringType)
        {
            if (info == null)
                throw new ArgumentNullException(nameof(info));
            this.Member = info;
            this.DeclaringType = declaringType;
            
        }
        IEnumerable<object> attributes = null;
 
        /// <summary> The attributes of this member. </summary>
        public IEnumerable<object> Attributes => attributes ?? (attributes = Member.GetCustomAttributes());
 
        static Type createDelegateType(MethodInfo method)
        {
            var parameters = method.GetParameters().Select(x => x.ParameterType);
            if (method.ReturnType != typeof(void))
                return Expression.GetFuncType(parameters.Append(method.ReturnType).ToArray());
            return Expression.GetActionType(parameters.ToArray());
        }
        
        static Func<object, object> buildGetter(PropertyInfo propertyInfo)
        {
            var instance = Expression.Parameter(typeof(object), "i");
            UnaryExpression convert1;
            if(propertyInfo.DeclaringType.IsValueType)
                convert1 = Expression.Convert(instance, propertyInfo.DeclaringType);
            else
                convert1 = Expression.TypeAs(instance, propertyInfo.DeclaringType);
            var property = Expression.Property(convert1, propertyInfo);
            var convert = Expression.TypeAs(property, typeof(object));
            
            var lambda = Expression.Lambda<Func<object, object>>(convert, instance);
            var action = lambda.Compile();
            return action;
        }
 
        Func<object, object> propertyGetter = null; 
 
        /// <summary> Gets the value of this member.</summary> 
        /// <param name="owner"></param>
        /// <returns></returns>
        public object GetValue(object owner)
        {
            switch (Member)
            {
                case PropertyInfo Property:
                    if(this.Readable == false) throw new Exception("Cannot get the value of a read-only property.");
                    if (propertyGetter == null)
                        propertyGetter = buildGetter(Property);
                    //Building a lambda expression is an order of magnitude faster than Property.GetValue.
                    return propertyGetter(owner);
                    
                case FieldInfo Field: return Field.GetValue(owner);
                case MethodInfo Method: return Delegate.CreateDelegate(createDelegateType(Method), owner, Method, true);
                default: throw new InvalidOperationException("Unsupported member type: " + Member);
            }
        }
 
        /// <summary>
        /// Sets the value of this member on an object.
        /// </summary>
        /// <param name="owner"></param>
        /// <param name="value"></param>
        public void SetValue(object owner, object value)
        {
            switch (Member)
            {
                case PropertyInfo Property:
                    Property.SetValue(owner, value);
                    break;
                case FieldInfo Field:
                    Field.SetValue(owner, value);
                    break;
                default:
                    throw new InvalidOperationException("Unsupported member type: " + Member);
            }
        }
 
        /// <summary> The name of this member. </summary>
        public string Name => Member.Name;
 
        /// <summary>
        /// The declaring type of this member.
        /// </summary>
        public ITypeData DeclaringType { get; }
 
        /// <summary> Gets if the member is writable. </summary>
        public bool Writable
        {
            get
            {
                switch (Member)
                {
                    case PropertyInfo Property: return Property.CanWrite && Property.GetSetMethod() != null;
                    case FieldInfo Field: return Field.IsInitOnly == false;
                    default: return false;
                }
            }
        }
 
        bool? readable; 
        
        /// <summary> Gets if the member is readable.  </summary>
        public bool Readable
        {
            get
            {
                switch (Member)
                {
                    case PropertyInfo Property: return (readable ?? (readable = Property.CanRead && Property.GetGetMethod() != null)).Value;
                    case FieldInfo _: return true;
                    case MethodInfo _: return true;
                    default: return false;
                }
            }
        }
 
        ITypeData typeDescriptor;
 
        /// <summary> The type descriptor for the object that this member can hold. </summary>
        public ITypeData TypeDescriptor => typeDescriptor ?? (typeDescriptor = getTypeDescriptor());
        
        ITypeData getTypeDescriptor()
        {
            switch (Member)
            {
                case PropertyInfo Property: return TypeData.FromType(Property.PropertyType);
                case FieldInfo Field: return TypeData.FromType(Field.FieldType);
                case MethodInfo Method: return TypeData.FromType(createDelegateType(Method));
                default: throw new InvalidOperationException("Unsupported member type: " + Member);
            }   
        }
 
        /// <summary> Gets a string representation of this CSharpType. </summary>
        public override string ToString() => $"[{Name}]";
 
        /// <summary> Equality for MemberData. Returns true if the other object is a MemberData and refers to same member object. </summary>
        public override bool Equals(object obj)
        {
            if (obj is MemberData m && m.Member == Member)
                return true;
            return false;
        }
        /// <summary> GetHash for MemberData. </summary>
        public override int GetHashCode() => Member.GetHashCode() * 32143211 + 88776366;
        
    }
}