PageRenderTime 38ms CodeModel.GetById 12ms RepoModel.GetById 0ms app.codeStats 1ms

/src/ServiceStack.Common/Utils/ReflectionUtils.cs

http://github.com/ServiceStack/ServiceStack
C# | 437 lines | 349 code | 70 blank | 18 comment | 94 complexity | d7380e0a8778fb2cdaa4a50a21be950b MD5 | raw file
Possible License(s): BSD-3-Clause
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Reflection;
  5. using System.Reflection.Emit;
  6. using ServiceStack.Common.Support;
  7. using ServiceStack.Logging;
  8. using ServiceStack.Net30.Collections.Concurrent;
  9. using ServiceStack.Text;
  10. namespace ServiceStack.Common.Utils
  11. {
  12. public class ReflectionUtils
  13. {
  14. public static readonly ILog Log = LogManager.GetLogger(typeof(ReflectionUtils));
  15. /// <summary>
  16. /// Populate an object with Example data.
  17. /// </summary>
  18. /// <param name="obj"></param>
  19. /// <returns></returns>
  20. public static object PopulateObject(object obj)
  21. {
  22. if (obj == null) return null;
  23. var type = obj.GetType();
  24. if (type.IsArray || type.IsValueType || type.IsGenericType)
  25. {
  26. var value = CreateDefaultValue(type, new Dictionary<Type, int>(20));
  27. return value;
  28. }
  29. return PopulateObjectInternal(obj, new Dictionary<Type, int>(20));
  30. }
  31. /// <summary>
  32. /// Populates the object with example data.
  33. /// </summary>
  34. /// <param name="obj"></param>
  35. /// <param name="recursionInfo">Tracks how deeply nested we are</param>
  36. /// <returns></returns>
  37. private static object PopulateObjectInternal(object obj, Dictionary<Type,int> recursionInfo)
  38. {
  39. if (obj == null) return null;
  40. if (obj is string) return obj; // prevents it from dropping into the char[] Chars property. Sheesh
  41. var type = obj.GetType();
  42. var members = type.GetMembers(BindingFlags.Public | BindingFlags.Instance);
  43. foreach (var info in members)
  44. {
  45. var fieldInfo = info as FieldInfo;
  46. var propertyInfo = info as PropertyInfo;
  47. if (fieldInfo != null || propertyInfo != null)
  48. {
  49. var memberType = fieldInfo != null ? fieldInfo.FieldType : propertyInfo.PropertyType;
  50. var value = CreateDefaultValue(memberType, recursionInfo);
  51. SetValue(fieldInfo, propertyInfo, obj, value);
  52. }
  53. }
  54. return obj;
  55. }
  56. private static readonly Dictionary<Type, object> DefaultValueTypes
  57. = new Dictionary<Type, object>();
  58. public static object GetDefaultValue(Type type)
  59. {
  60. if (!type.IsValueType) return null;
  61. object defaultValue;
  62. lock (DefaultValueTypes)
  63. {
  64. if (!DefaultValueTypes.TryGetValue(type, out defaultValue))
  65. {
  66. defaultValue = Activator.CreateInstance(type);
  67. DefaultValueTypes[type] = defaultValue;
  68. }
  69. }
  70. return defaultValue;
  71. }
  72. private static readonly ConcurrentDictionary<string, AssignmentDefinition> AssignmentDefinitionCache
  73. = new ConcurrentDictionary<string, AssignmentDefinition>();
  74. public static AssignmentDefinition GetAssignmentDefinition(Type toType, Type fromType)
  75. {
  76. var cacheKey = toType.FullName + "<" + fromType.FullName;
  77. return AssignmentDefinitionCache.GetOrAdd(cacheKey, delegate {
  78. var definition = new AssignmentDefinition {
  79. ToType = toType,
  80. FromType = fromType,
  81. };
  82. var members = fromType.GetMembers(BindingFlags.Public | BindingFlags.Instance);
  83. foreach (var info in members)
  84. {
  85. var fromPropertyInfo = info as PropertyInfo;
  86. if (fromPropertyInfo != null)
  87. {
  88. var toPropertyInfo = GetPropertyInfo(toType, fromPropertyInfo.Name);
  89. if (toPropertyInfo == null) continue;
  90. if (!fromPropertyInfo.CanRead) continue;
  91. if (!toPropertyInfo.CanWrite) continue;
  92. definition.AddMatch(fromPropertyInfo, toPropertyInfo);
  93. }
  94. var fromFieldInfo = info as FieldInfo;
  95. if (fromFieldInfo != null)
  96. {
  97. var toFieldInfo = GetFieldInfo(toType, fromFieldInfo.Name);
  98. if (toFieldInfo == null) continue;
  99. definition.AddMatch(fromFieldInfo, toFieldInfo);
  100. }
  101. }
  102. return definition;
  103. });
  104. }
  105. public static To PopulateObject<To, From>(To to, From from)
  106. {
  107. if (Equals(to, default(To)) || Equals(from, default(From))) return default(To);
  108. var assignmentDefinition = GetAssignmentDefinition(to.GetType(), from.GetType());
  109. assignmentDefinition.Populate(to, from);
  110. return to;
  111. }
  112. public static To PopulateWithNonDefaultValues<To, From>(To to, From from)
  113. {
  114. if (Equals(to, default(To)) || Equals(from, default(From))) return default(To);
  115. var assignmentDefinition = GetAssignmentDefinition(to.GetType(), from.GetType());
  116. assignmentDefinition.PopulateWithNonDefaultValues(to, from);
  117. return to;
  118. }
  119. public static To PopulateFromPropertiesWithAttribute<To, From>(To to, From from,
  120. Type attributeType)
  121. {
  122. if (Equals(to, default(To)) || Equals(from, default(From))) return default(To);
  123. var assignmentDefinition = GetAssignmentDefinition(to.GetType(), from.GetType());
  124. assignmentDefinition.PopulateFromPropertiesWithAttribute(to, from, attributeType);
  125. return to;
  126. }
  127. public static void SetProperty(object obj, PropertyInfo propertyInfo, object value)
  128. {
  129. if (!propertyInfo.CanWrite)
  130. {
  131. Log.WarnFormat("Attempted to set read only property '{0}'", propertyInfo.Name);
  132. return;
  133. }
  134. var propertySetMetodInfo = propertyInfo.GetSetMethod();
  135. if (propertySetMetodInfo != null)
  136. {
  137. propertySetMetodInfo.Invoke(obj, new[] { value });
  138. }
  139. }
  140. public static object GetProperty(object obj, PropertyInfo propertyInfo)
  141. {
  142. if (propertyInfo == null || !propertyInfo.CanRead)
  143. return null;
  144. var getMethod = propertyInfo.GetGetMethod();
  145. return getMethod != null ? getMethod.Invoke(obj, new object[0]) : null;
  146. }
  147. public static void SetValue(FieldInfo fieldInfo, PropertyInfo propertyInfo, object obj, object value)
  148. {
  149. try
  150. {
  151. if (IsUnsettableValue(fieldInfo, propertyInfo)) return;
  152. if (fieldInfo != null && !fieldInfo.IsLiteral)
  153. {
  154. fieldInfo.SetValue(obj, value);
  155. }
  156. else
  157. {
  158. SetProperty(obj, propertyInfo, value);
  159. }
  160. }
  161. catch (Exception ex)
  162. {
  163. var name = (fieldInfo != null) ? fieldInfo.Name : propertyInfo.Name;
  164. Log.DebugFormat("Could not set member: {0}. Error: {1}", name, ex.Message);
  165. }
  166. }
  167. public static bool IsUnsettableValue(FieldInfo fieldInfo, PropertyInfo propertyInfo)
  168. {
  169. if (propertyInfo != null && propertyInfo.ReflectedType != null)
  170. {
  171. // Properties on non-user defined classes should not be set
  172. // Currently we define those properties as properties declared on
  173. // types defined in mscorlib
  174. if (propertyInfo.DeclaringType.Assembly == typeof(object).Assembly)
  175. {
  176. return true;
  177. }
  178. }
  179. return false;
  180. }
  181. public static object[] CreateDefaultValues(IEnumerable<Type> types, Dictionary<Type, int> recursionInfo)
  182. {
  183. var values = new List<object>();
  184. foreach (var type in types)
  185. {
  186. values.Add(CreateDefaultValue(type, recursionInfo));
  187. }
  188. return values.ToArray();
  189. }
  190. private const int MaxRecursionLevelForDefaultValues = 2; // do not nest a single type more than this deep.
  191. public static object CreateDefaultValue(Type type, Dictionary<Type, int> recursionInfo)
  192. {
  193. if (type == typeof(string))
  194. {
  195. return type.Name;
  196. }
  197. if (type.IsEnum)
  198. {
  199. #if SILVERLIGHT4 || WINDOWS_PHONE
  200. return Enum.ToObject(type, 0);
  201. #else
  202. return Enum.GetValues(type).GetValue(0);
  203. #endif
  204. }
  205. // If we have hit our recursion limit for this type, then return null
  206. int recurseLevel; // will get set to 0 if TryGetValue() fails
  207. recursionInfo.TryGetValue(type, out recurseLevel);
  208. if (recurseLevel > MaxRecursionLevelForDefaultValues) return null;
  209. recursionInfo[type] = recurseLevel + 1; // increase recursion level for this type
  210. try // use a try/finally block to make sure we decrease the recursion level for this type no matter which code path we take,
  211. {
  212. //when using KeyValuePair<TKey, TValue>, TKey must be non-default to stuff in a Dictionary
  213. if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(KeyValuePair<,>))
  214. {
  215. var genericTypes = type.GetGenericArguments();
  216. var valueType = Activator.CreateInstance(type, CreateDefaultValue(genericTypes[0], recursionInfo), CreateDefaultValue(genericTypes[1], recursionInfo));
  217. return PopulateObjectInternal(valueType, recursionInfo);
  218. }
  219. if (type.IsValueType)
  220. {
  221. return type.CreateInstance();
  222. }
  223. if (type.IsArray)
  224. {
  225. return PopulateArray(type, recursionInfo);
  226. }
  227. var constructorInfo = type.GetConstructor(Type.EmptyTypes);
  228. var hasEmptyConstructor = constructorInfo != null;
  229. if (hasEmptyConstructor)
  230. {
  231. var value = constructorInfo.Invoke(new object[0]);
  232. #if !SILVERLIGHT && !MONOTOUCH && !XBOX
  233. var genericCollectionType = GetGenericCollectionType(type);
  234. if (genericCollectionType != null)
  235. {
  236. SetGenericCollection(genericCollectionType, value, recursionInfo);
  237. }
  238. #endif
  239. //when the object might have nested properties such as enums with non-0 values, etc
  240. return PopulateObjectInternal(value, recursionInfo);
  241. }
  242. return null;
  243. }
  244. finally
  245. {
  246. recursionInfo[type] = recurseLevel;
  247. }
  248. }
  249. private static Type GetGenericCollectionType(Type type)
  250. {
  251. #if WINDOWS_PHONE
  252. var genericCollectionType =
  253. type.GetInterfaces()
  254. .FirstOrDefault(t => t.IsGenericType && t.GetGenericTypeDefinition() == typeof (ICollection<>));
  255. #else
  256. var genericCollectionType = type.FindInterfaces((t, critera) =>
  257. t.IsGenericType
  258. && t.GetGenericTypeDefinition() == typeof (ICollection<>), null).FirstOrDefault();
  259. #endif
  260. return genericCollectionType;
  261. }
  262. public static void SetGenericCollection(Type realisedListType, object genericObj, Dictionary<Type, int> recursionInfo)
  263. {
  264. var args = realisedListType.GetGenericArguments();
  265. if (args.Length != 1)
  266. {
  267. Log.ErrorFormat("Found a generic list that does not take one generic argument: {0}", realisedListType);
  268. return;
  269. }
  270. var methodInfo = realisedListType.GetMethod("Add");
  271. if (methodInfo != null)
  272. {
  273. var argValues = CreateDefaultValues(args, recursionInfo);
  274. methodInfo.Invoke(genericObj, argValues);
  275. }
  276. }
  277. public static Array PopulateArray(Type type, Dictionary<Type, int> recursionInfo)
  278. {
  279. var elementType = type.GetElementType();
  280. var objArray = Array.CreateInstance(elementType, 1);
  281. var objElementType = CreateDefaultValue(elementType, recursionInfo);
  282. objArray.SetValue(objElementType, 0);
  283. return objArray;
  284. }
  285. //TODO: replace with InAssignableFrom
  286. public static bool CanCast(Type toType, Type fromType)
  287. {
  288. if (toType.IsInterface)
  289. {
  290. var interfaceList = fromType.GetInterfaces().ToList();
  291. if (interfaceList.Contains(toType)) return true;
  292. }
  293. else
  294. {
  295. Type baseType = fromType;
  296. bool areSameTypes;
  297. do
  298. {
  299. areSameTypes = baseType == toType;
  300. }
  301. while (!areSameTypes && (baseType = fromType.BaseType) != null);
  302. if (areSameTypes) return true;
  303. }
  304. return false;
  305. }
  306. public static MemberInfo GetMemberInfo(Type fromType, string memberName)
  307. {
  308. var baseType = fromType;
  309. do
  310. {
  311. var members = baseType.GetMembers(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
  312. foreach (var memberInfo in members)
  313. {
  314. if (memberInfo.Name == memberName) return memberInfo;
  315. }
  316. }
  317. while ((baseType = baseType.BaseType) != null);
  318. return null;
  319. }
  320. public static FieldInfo GetFieldInfo(Type fromType, string fieldName)
  321. {
  322. var baseType = fromType;
  323. do
  324. {
  325. var fieldInfos = baseType.GetFields(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
  326. foreach (var fieldInfo in fieldInfos)
  327. {
  328. if (fieldInfo.Name == fieldName) return fieldInfo;
  329. }
  330. }
  331. while ((baseType = baseType.BaseType) != null);
  332. return null;
  333. }
  334. public static PropertyInfo GetPropertyInfo(Type fromType, string propertyName)
  335. {
  336. var baseType = fromType;
  337. do
  338. {
  339. var propertyInfos = baseType.GetProperties(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
  340. foreach (var propertyInfo in propertyInfos)
  341. {
  342. if (propertyInfo.Name == propertyName) return propertyInfo;
  343. }
  344. }
  345. while ((baseType = baseType.BaseType) != null);
  346. return null;
  347. }
  348. public static IEnumerable<KeyValuePair<PropertyInfo, T>> GetPropertyAttributes<T>(Type fromType) where T : Attribute
  349. {
  350. var attributeType = typeof(T);
  351. var baseType = fromType;
  352. do
  353. {
  354. var propertyInfos = baseType.GetProperties(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
  355. foreach (var propertyInfo in propertyInfos)
  356. {
  357. var attributes = propertyInfo.GetCustomAttributes(attributeType, true);
  358. foreach (T attribute in attributes)
  359. {
  360. yield return new KeyValuePair<PropertyInfo, T>(propertyInfo, attribute);
  361. }
  362. }
  363. }
  364. while ((baseType = baseType.BaseType) != null);
  365. }
  366. }
  367. }