PropertyReference.cs 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199
  1. using System.Collections.Generic;
  2. using System.ComponentModel;
  3. using JetBrains.Annotations;
  4. using UnityEngine;
  5. namespace SRF.Helpers
  6. {
  7. using System;
  8. using System.Linq;
  9. using System.Reflection;
  10. public delegate void PropertyValueChangedHandler(PropertyReference property);
  11. public sealed class PropertyReference
  12. {
  13. public event PropertyValueChangedHandler ValueChanged
  14. {
  15. add
  16. {
  17. if (_valueChangedListeners == null)
  18. {
  19. _valueChangedListeners = new List<PropertyValueChangedHandler>();
  20. }
  21. _valueChangedListeners.Add(value);
  22. if (_valueChangedListeners.Count == 1 && _target is INotifyPropertyChanged)
  23. {
  24. // Subscribe to value changed event on target.
  25. ((INotifyPropertyChanged)_target).PropertyChanged += OnTargetPropertyChanged;
  26. }
  27. }
  28. remove
  29. {
  30. if (_valueChangedListeners == null)
  31. {
  32. return;
  33. }
  34. if (_valueChangedListeners.Remove(value) && _valueChangedListeners.Count == 0 &&
  35. _target is INotifyPropertyChanged)
  36. {
  37. // Unsubscribe from value changed event on target.
  38. ((INotifyPropertyChanged) _target).PropertyChanged -= OnTargetPropertyChanged;
  39. }
  40. }
  41. }
  42. [CanBeNull] private readonly PropertyInfo _property;
  43. [CanBeNull] private readonly object _target;
  44. [CanBeNull] private readonly Attribute[] _attributes;
  45. [CanBeNull] private readonly Func<object> _getter;
  46. [CanBeNull] private readonly Action<object> _setter;
  47. [CanBeNull] private List<PropertyValueChangedHandler> _valueChangedListeners;
  48. public static PropertyReference FromLambda<T>(Func<T> getter, Action<T> setter = null, params Attribute[] attributes)
  49. {
  50. Action<object> internalSetter = null;
  51. if (setter != null)
  52. {
  53. internalSetter = o => setter((T)o);
  54. }
  55. return new PropertyReference(typeof(T), () => getter(), internalSetter, attributes);
  56. }
  57. /// <summary>
  58. /// Create a property reference from an object target and reflection PropertyInfo.
  59. /// This represents a property on an object.
  60. /// </summary>
  61. public PropertyReference(object target, PropertyInfo property)
  62. {
  63. SRDebugUtil.AssertNotNull(target);
  64. SRDebugUtil.AssertNotNull(property);
  65. PropertyType = property.PropertyType;
  66. _property = property;
  67. _target = target;
  68. #if NETFX_CORE
  69. if(_property.GetMethod != null && _property.GetMethod.IsPublic)
  70. #else
  71. if (property.GetGetMethod() != null)
  72. #endif
  73. {
  74. _getter = () => SRReflection.GetPropertyValue(target, property);
  75. }
  76. #if NETFX_CORE
  77. if(_property.SetMethod != null && _property.SetMethod.IsPublic)
  78. #else
  79. if (property.GetSetMethod() != null)
  80. #endif
  81. {
  82. _setter = (v) => SRReflection.SetPropertyValue(target, property, v);
  83. }
  84. }
  85. /// <summary>
  86. /// Create a property reference from lambdas. This has no underlying reflection or object associated with it.
  87. /// </summary>
  88. public PropertyReference(Type type, Func<object> getter = null, Action<object> setter = null, Attribute[] attributes = null)
  89. {
  90. SRDebugUtil.AssertNotNull(type);
  91. PropertyType = type;
  92. _attributes = attributes;
  93. _getter = getter;
  94. _setter = setter;
  95. }
  96. public Type PropertyType { get; private set; }
  97. public bool CanRead
  98. {
  99. get
  100. {
  101. return _getter != null;
  102. }
  103. }
  104. public bool CanWrite
  105. {
  106. get
  107. {
  108. return _setter != null;
  109. }
  110. }
  111. /// <summary>
  112. /// Notify any listeners to <see cref="ValueChanged"/> that the value has been updated.
  113. /// </summary>
  114. public void NotifyValueChanged()
  115. {
  116. if (_valueChangedListeners == null)
  117. {
  118. return;
  119. }
  120. foreach (var handler in _valueChangedListeners)
  121. {
  122. handler(this);
  123. }
  124. }
  125. public object GetValue()
  126. {
  127. if (_getter != null)
  128. {
  129. return _getter();
  130. }
  131. return null;
  132. }
  133. public void SetValue(object value)
  134. {
  135. if (_setter != null)
  136. {
  137. _setter(value);
  138. }
  139. else
  140. {
  141. throw new InvalidOperationException("Can not write to property");
  142. }
  143. }
  144. public T GetAttribute<T>() where T : Attribute
  145. {
  146. if (_attributes != null)
  147. {
  148. return _attributes.FirstOrDefault(p => p is T) as T;
  149. }
  150. if (_property != null)
  151. {
  152. return _property.GetCustomAttributes(typeof(T), true).FirstOrDefault() as T;
  153. }
  154. return null;
  155. }
  156. private void OnTargetPropertyChanged(object sender, PropertyChangedEventArgs e)
  157. {
  158. if (_valueChangedListeners == null || _valueChangedListeners.Count == 0)
  159. {
  160. Debug.LogWarning("[PropertyReference] Received property value changed event when there are no listeners. Did the event not get unsubscribed correctly?");
  161. return;
  162. }
  163. NotifyValueChanged();
  164. }
  165. }
  166. }