C# Client Library
A C# Client Library for the AnalyzeRe REST API
Loading...
Searching...
No Matches
ReflectionUtilities.cs
Go to the documentation of this file.
1using System;
2using System.Collections.Concurrent;
3using System.Collections.Generic;
4using System.Linq;
5using System.Linq.Expressions;
6using System.Reflection;
7using System.Runtime.Serialization;
8
10
11namespace AnalyzeRe.Utilities
12{
14 public static class ReflectionUtilities
15 {
17 public const BindingFlags PublicInstanceBindingFlags =
18 BindingFlags.FlattenHierarchy | BindingFlags.Public | BindingFlags.Instance;
19
27 public static string GetPropertyName<TObject>(Expression<Func<TObject, object>> propertyExpression) =>
28 GetMemberInfo(propertyExpression).Name;
29
35 public static MemberInfo GetMemberInfo<T>(Expression<T> expression)
36 {
37 Expression body = expression.Body;
38 // If this is a chained expression, get the final property referenced
39 while (body is UnaryExpression unaryExpression)
40 body = unaryExpression.Operand;
41 if (body is MemberExpression memberExpression)
42 return memberExpression.Member;
43 throw new ArgumentException("No MemberExpression found in " + expression.GetType().NiceTypeName());
44 }
45
47 public static bool IsSubclassOfRawGeneric(this Type toCheck, Type generic)
48 {
49 while (toCheck != null && toCheck != typeof(object))
50 {
51 Type cur = toCheck.IsGenericType ? toCheck.GetGenericTypeDefinition() : toCheck;
52 if (generic == cur)
53 return true;
54 toCheck = toCheck.BaseType;
55 }
56 return false;
57 }
58
65 public static bool PropertyHasAttribute<T>(Expression<T> expression, Type attributeType)
66 {
67 // Automatically determine from presence of the validation attribute.
68 MemberInfo propertyInfo = GetMemberInfo(expression);
69 return propertyInfo.IsAttributeDefinedFast(attributeType);
70 }
71
72 #region Cached Attribute Retrieval
74 private static readonly ConcurrentDictionary<Tuple<string, Type, Type>, object>
75 CachedPropertyAttributes = new ConcurrentDictionary<Tuple<string, Type, Type>, object>();
76
81 public static bool IsAttributeDefinedFast<T>(this MemberInfo member)
82 {
83 return IsAttributeDefinedFast(member, typeof(T));
84 }
85
90 public static bool IsAttributeDefinedFast(this MemberInfo member, Type attributeType)
91 {
92 return member.GetCustomAttributeFast(attributeType) != null;
93 }
94
99 public static T GetCustomAttributeFast<T>(this MemberInfo member)
100 {
101 return (T)GetCustomAttributeFast(member, typeof(T));
102 }
103
108 public static object GetCustomAttributeFast(this MemberInfo member, Type attributeType)
109 {
110 Tuple<string, Type, Type> cacheKey =
111 Tuple.Create(member.Name, member.DeclaringType, attributeType);
112 object result = CachedPropertyAttributes.GetOrAdd(cacheKey, key =>
113 {
114 try { return Attribute.GetCustomAttribute(member, attributeType, true); }
115 catch (Exception ex) { return ex; }
116 });
117 if (result is Exception exceptionResult)
118 throw exceptionResult;
119 return result;
120 }
121 #endregion Cached Attribute Retrieval
122
123 #region GetUserFacingProperties
127 private static readonly ConcurrentDictionary<Tuple<Type, bool, bool, bool>, PropertyInfo[]>
128 CachedUserFacingProperties = new ConcurrentDictionary<Tuple<Type, bool, bool, bool>, PropertyInfo[]>();
129
147 public static PropertyInfo[] GetUserFacingProperties(this Type type,
148 bool excludeServerGenerated = false,
149 bool excludeNotWritable = false,
150 bool excludeNotSerialized = false)
151 {
152 if (type == null)
153 throw new ArgumentNullException(nameof(type), "Could not determine the declaring type.");
154
155 return CachedUserFacingProperties.GetOrAdd(
156 Tuple.Create(type, excludeServerGenerated, excludeNotWritable, excludeNotSerialized),
157 CachedUserFacingProperties_OnAdd);
158 }
159
161 private static PropertyInfo[] CachedUserFacingProperties_OnAdd(
162 Tuple<Type, bool, bool, bool> key)
163 {
164 Type type = key.Item1;
165 bool excludeServerGenerated = key.Item2;
166 bool excludeNotWritable = key.Item3;
167 bool excludeNotSerialized = key.Item4;
168
169 IEnumerable<PropertyInfo> filtered = GetPublicProperties_Fast(type)
170 .Where(pi => pi.CanRead &&
171 !pi.IsAttributeDefinedFast<InternalMemberAttribute>() &&
172 !pi.IsAttributeDefinedFast<ObsoleteAttribute>());
173 if(excludeServerGenerated)
174 filtered = filtered.Where(pi => !pi.IsAttributeDefinedFast<ServerGenerated>());
175 if (excludeNotWritable)
176 filtered = filtered.Where(pi => pi.CanWrite);
177 if (excludeNotSerialized)
178 filtered = filtered.Where(pi => !pi.IsAttributeDefinedFast<IgnoreDataMemberAttribute>());
179
180 // Iterate over all properties that meet the specified criteria, adding them to a
181 // dictionary to enforce uniqueness by property name.
182 // If the list of properties already contains a property name, use the propertyInfo
183 // from the type that inherits from (supersedes) the other.
184 Dictionary<string, PropertyInfo> propertiesByName = new Dictionary<string, PropertyInfo>();
185 foreach (PropertyInfo property in filtered)
186 {
187 if (!propertiesByName.TryGetValue(property.Name, out PropertyInfo conflicting) ||
188 conflicting.DeclaringType.IsAssignableFrom(property.DeclaringType))
189 propertiesByName[property.Name] = property;
190 }
191
192 return propertiesByName.Values.ToArray();
193 }
194 #endregion GetUserFacingProperties
195
196 #region GetPublicProperties
198 private static readonly ConcurrentDictionary<Type, PropertyInfo[]> CachedPublicProperties =
199 new ConcurrentDictionary<Type, PropertyInfo[]>();
200
206 public static PropertyInfo[] GetPublicProperties_Fast(this Type type)
207 {
208 if (type == null)
209 throw new ArgumentNullException(nameof(type), "Could not determine the declaring type.");
210 return CachedPublicProperties.GetOrAdd(type, GetPublicProperties_Fast_OnAdd);
211 }
212
214 private static PropertyInfo[] GetPublicProperties_Fast_OnAdd(Type type)
215 {
216 // Simple case, instantiable types have a method for this
217 if (!type.IsInterface)
218 return type.GetProperties(PublicInstanceBindingFlags);
219 // Interfaces take more work
220 List<PropertyInfo> propertyInfos = new List<PropertyInfo>();
221 List<Type> considered = new List<Type>();
222 Queue<Type> queue = new Queue<Type>();
223 considered.Add(type);
224 queue.Enqueue(type);
225 while (queue.Count > 0)
226 {
227 Type subType = queue.Dequeue();
228 foreach (Type subInterface in subType.GetInterfaces()
229 .Where(subInterface => !considered.Contains(subInterface)))
230 {
231 considered.Add(subInterface);
232 queue.Enqueue(subInterface);
233 }
234 IEnumerable<PropertyInfo> newPropertyInfos =
235 subType.GetProperties(PublicInstanceBindingFlags)
236 .Where(x => !propertyInfos.Contains(x));
237 propertyInfos.InsertRange(0, newPropertyInfos);
238 }
239 return propertyInfos.ToArray();
240 }
241 #endregion GetPublicProperties
242
243 #region GetPublicFields
245 private static readonly ConcurrentDictionary<Type, FieldInfo[]> CachedPublicFields =
246 new ConcurrentDictionary<Type, FieldInfo[]>();
247
253 public static FieldInfo[] GetPublicFields_Fast(this Type type)
254 {
255 if (type == null)
256 throw new ArgumentNullException(nameof(type), "Could not determine the declaring type.");
257 // Interfaces cannot define fields.
258 return type.IsInterface ? new FieldInfo[] { } :
259 CachedPublicFields.GetOrAdd(type, t => t.GetFields(PublicInstanceBindingFlags));
260 }
261 #endregion GetPublicFields
262
266 public static T ShallowCopy<T>(T toCopy)
267 {
268 T obj = Activator.CreateInstance<T>();
269 // Copy over all public gettable and settable properties
270 foreach (PropertyInfo propertyInfo in typeof(T).GetPublicProperties_Fast()
271 .Where(p => p.CanWrite))
272 propertyInfo.SetValue(obj, propertyInfo.GetValue(toCopy, null), null);
273 // Copy over all public gettable and settable fields
274 foreach (FieldInfo fieldInfo in typeof(T).GetPublicFields_Fast())
275 fieldInfo.SetValue(obj, fieldInfo.GetValue(toCopy));
276 return obj;
277 }
278 }
279}
This attribute is used on IAPIType classes to indicate members (especially public properties) that ex...
Specifies that a property is generated by the server and should not be specified on the client side d...
Utilities that reflect on a type or property expression.
const BindingFlags PublicInstanceBindingFlags
Flags for retrieving all declared and inherited public instance members.
static T ShallowCopy< T >(T toCopy)
Attempt to make a shallow copy of the specified object by reflecting on its public properties.
static FieldInfo[] GetPublicFields_Fast(this Type type)
Get all public instance fields of a type, and cache the result so that future reflection on the same ...
static T GetCustomAttributeFast< T >(this MemberInfo member)
Gets the specified attribute from the member.
static string GetPropertyName< TObject >(Expression< Func< TObject, object > > propertyExpression)
Gets the string name of a property from a LINQ expression such as GetPropertyName( ()=>SomeProperty )...
static PropertyInfo[] GetPublicProperties_Fast(this Type type)
Get all public instance properties of a type, and cache the result so that future reflection on the s...
static bool IsSubclassOfRawGeneric(this Type toCheck, Type generic)
Checks if a type is a subclass of a generic type.
static bool PropertyHasAttribute< T >(Expression< T > expression, Type attributeType)
Determines whether the property denoted by the expression has the specified attribute defined.
static PropertyInfo[] GetUserFacingProperties(this Type type, bool excludeServerGenerated=false, bool excludeNotWritable=false, bool excludeNotSerialized=false)
Gets the "user-facing" PropertyInfo, which has been filtered from the set of all public properties of...
static bool IsAttributeDefinedFast(this MemberInfo member, Type attributeType)
Determines whether the member has the specified attribute defined.
static bool IsAttributeDefinedFast< T >(this MemberInfo member)
Determines whether the member has the specified attribute defined.
static MemberInfo GetMemberInfo< T >(Expression< T > expression)
Get the MemberInfo associated with an Expression, if possible.
static object GetCustomAttributeFast(this MemberInfo member, Type attributeType)
Gets the specified attribute from the member.