Traverse.cs 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Globalization;
  4. using System.Reflection;
  5. using System.Runtime.CompilerServices;
  6. namespace Harmony
  7. {
  8. public class Traverse<T>
  9. {
  10. private Traverse traverse;
  11. Traverse()
  12. {
  13. }
  14. public Traverse(Traverse traverse)
  15. {
  16. this.traverse = traverse;
  17. }
  18. public T Value
  19. {
  20. get => traverse.GetValue<T>();
  21. set => traverse.SetValue(value);
  22. }
  23. }
  24. public class Traverse
  25. {
  26. static AccessCache Cache;
  27. Type _type;
  28. object _root;
  29. MemberInfo _info;
  30. MethodBase _method;
  31. object[] _params;
  32. [MethodImpl(MethodImplOptions.Synchronized)]
  33. static Traverse()
  34. {
  35. if (Cache == null)
  36. Cache = new AccessCache();
  37. }
  38. public static Traverse Create(Type type)
  39. {
  40. return new Traverse(type);
  41. }
  42. public static Traverse Create<T>()
  43. {
  44. return Create(typeof(T));
  45. }
  46. public static Traverse Create(object root)
  47. {
  48. return new Traverse(root);
  49. }
  50. public static Traverse CreateWithType(string name)
  51. {
  52. return new Traverse(AccessTools.TypeByName(name));
  53. }
  54. Traverse()
  55. {
  56. }
  57. public Traverse(Type type)
  58. {
  59. _type = type;
  60. }
  61. public Traverse(object root)
  62. {
  63. _root = root;
  64. _type = root?.GetType();
  65. }
  66. Traverse(object root, MemberInfo info, object[] index)
  67. {
  68. _root = root;
  69. _type = root?.GetType();
  70. _info = info;
  71. _params = index;
  72. }
  73. Traverse(object root, MethodInfo method, object[] parameter)
  74. {
  75. _root = root;
  76. _type = method.ReturnType;
  77. _method = method;
  78. _params = parameter;
  79. }
  80. public object GetValue()
  81. {
  82. if (_info is FieldInfo)
  83. return ((FieldInfo)_info).GetValue(_root);
  84. if (_info is PropertyInfo)
  85. return ((PropertyInfo)_info).GetValue(_root, AccessTools.all, null, _params, CultureInfo.CurrentCulture);
  86. if (_method != null)
  87. return _method.Invoke(_root, _params);
  88. if (_root == null && _type != null) return _type;
  89. return _root;
  90. }
  91. public T GetValue<T>()
  92. {
  93. var value = GetValue();
  94. if (value == null) return default(T);
  95. return (T)value;
  96. }
  97. public object GetValue(params object[] arguments)
  98. {
  99. if (_method == null)
  100. throw new Exception("cannot get method value without method");
  101. return _method.Invoke(_root, arguments);
  102. }
  103. public T GetValue<T>(params object[] arguments)
  104. {
  105. if (_method == null)
  106. throw new Exception("cannot get method value without method");
  107. return (T)_method.Invoke(_root, arguments);
  108. }
  109. public Traverse SetValue(object value)
  110. {
  111. if (_info is FieldInfo)
  112. ((FieldInfo)_info).SetValue(_root, value, AccessTools.all, null, CultureInfo.CurrentCulture);
  113. if (_info is PropertyInfo)
  114. ((PropertyInfo)_info).SetValue(_root, value, AccessTools.all, null, _params, CultureInfo.CurrentCulture);
  115. if (_method != null)
  116. throw new Exception("cannot set value of method " + _method.FullDescription());
  117. return this;
  118. }
  119. public Type GetValueType()
  120. {
  121. if (_info is FieldInfo)
  122. return ((FieldInfo)_info).FieldType;
  123. if (_info is PropertyInfo)
  124. return ((PropertyInfo)_info).PropertyType;
  125. return null;
  126. }
  127. Traverse Resolve()
  128. {
  129. if (_root == null && _type != null) return this;
  130. return new Traverse(GetValue());
  131. }
  132. public Traverse Type(string name)
  133. {
  134. if (name == null) throw new ArgumentNullException("name cannot be null");
  135. if (_type == null) return new Traverse();
  136. var type = AccessTools.Inner(_type, name);
  137. if (type == null) return new Traverse();
  138. return new Traverse(type);
  139. }
  140. public Traverse Field(string name)
  141. {
  142. if (name == null) throw new ArgumentNullException("name cannot be null");
  143. var resolved = Resolve();
  144. if (resolved._type == null) return new Traverse();
  145. var info = Cache.GetFieldInfo(resolved._type, name);
  146. if (info == null) return new Traverse();
  147. if (info.IsStatic == false && resolved._root == null) return new Traverse();
  148. return new Traverse(resolved._root, info, null);
  149. }
  150. public Traverse<T> Field<T>(string name)
  151. {
  152. return new Traverse<T>(Field(name));
  153. }
  154. public List<string> Fields()
  155. {
  156. var resolved = Resolve();
  157. return AccessTools.GetFieldNames(resolved._type);
  158. }
  159. public Traverse Property(string name, object[] index = null)
  160. {
  161. if (name == null) throw new ArgumentNullException("name cannot be null");
  162. var resolved = Resolve();
  163. if (resolved._root == null || resolved._type == null) return new Traverse();
  164. var info = Cache.GetPropertyInfo(resolved._type, name);
  165. if (info == null) return new Traverse();
  166. return new Traverse(resolved._root, info, index);
  167. }
  168. public Traverse<T> Property<T>(string name, object[] index = null)
  169. {
  170. return new Traverse<T>(Property(name, index));
  171. }
  172. public List<string> Properties()
  173. {
  174. var resolved = Resolve();
  175. return AccessTools.GetPropertyNames(resolved._type);
  176. }
  177. public Traverse Method(string name, params object[] arguments)
  178. {
  179. if (name == null) throw new ArgumentNullException("name cannot be null");
  180. var resolved = Resolve();
  181. if (resolved._type == null) return new Traverse();
  182. var types = AccessTools.GetTypes(arguments);
  183. var method = Cache.GetMethodInfo(resolved._type, name, types);
  184. if (method == null) return new Traverse();
  185. return new Traverse(resolved._root, (MethodInfo)method, arguments);
  186. }
  187. public Traverse Method(string name, Type[] paramTypes, object[] arguments = null)
  188. {
  189. if (name == null) throw new ArgumentNullException("name cannot be null");
  190. var resolved = Resolve();
  191. if (resolved._type == null) return new Traverse();
  192. var method = Cache.GetMethodInfo(resolved._type, name, paramTypes);
  193. if (method == null) return new Traverse();
  194. return new Traverse(resolved._root, (MethodInfo)method, arguments);
  195. }
  196. public List<string> Methods()
  197. {
  198. var resolved = Resolve();
  199. return AccessTools.GetMethodNames(resolved._type);
  200. }
  201. public bool FieldExists()
  202. {
  203. return _info != null;
  204. }
  205. public bool MethodExists()
  206. {
  207. return _method != null;
  208. }
  209. public bool TypeExists()
  210. {
  211. return _type != null;
  212. }
  213. public static void IterateFields(object source, Action<Traverse> action)
  214. {
  215. var sourceTrv = Create(source);
  216. AccessTools.GetFieldNames(source).ForEach(f => action(sourceTrv.Field(f)));
  217. }
  218. public static void IterateFields(object source, object target, Action<Traverse, Traverse> action)
  219. {
  220. var sourceTrv = Create(source);
  221. var targetTrv = Create(target);
  222. AccessTools.GetFieldNames(source).ForEach(f => action(sourceTrv.Field(f), targetTrv.Field(f)));
  223. }
  224. public static void IterateFields(object source, object target, Action<string, Traverse, Traverse> action)
  225. {
  226. var sourceTrv = Create(source);
  227. var targetTrv = Create(target);
  228. AccessTools.GetFieldNames(source).ForEach(f => action(f, sourceTrv.Field(f), targetTrv.Field(f)));
  229. }
  230. public static void IterateProperties(object source, Action<Traverse> action)
  231. {
  232. var sourceTrv = Create(source);
  233. AccessTools.GetPropertyNames(source).ForEach(f => action(sourceTrv.Property(f)));
  234. }
  235. public static void IterateProperties(object source, object target, Action<Traverse, Traverse> action)
  236. {
  237. var sourceTrv = Create(source);
  238. var targetTrv = Create(target);
  239. AccessTools.GetPropertyNames(source).ForEach(f => action(sourceTrv.Property(f), targetTrv.Property(f)));
  240. }
  241. public static void IterateProperties(object source, object target, Action<string, Traverse, Traverse> action)
  242. {
  243. var sourceTrv = Create(source);
  244. var targetTrv = Create(target);
  245. AccessTools.GetPropertyNames(source).ForEach(f => action(f, sourceTrv.Property(f), targetTrv.Property(f)));
  246. }
  247. public static Action<Traverse, Traverse> CopyFields = (from, to) => { to.SetValue(from.GetValue()); };
  248. public override string ToString()
  249. {
  250. var value = _method ?? GetValue();
  251. return value?.ToString();
  252. }
  253. }
  254. }