You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
CrowdControl/Assets/3rd/StompyRobot/SRF/Scripts/Helpers/PropertyReference.cs

200 lines
5.7 KiB
C#

3 months ago
using System.Collections.Generic;
using System.ComponentModel;
using JetBrains.Annotations;
using UnityEngine;
namespace SRF.Helpers
{
using System;
using System.Linq;
using System.Reflection;
public delegate void PropertyValueChangedHandler(PropertyReference property);
public sealed class PropertyReference
{
public event PropertyValueChangedHandler ValueChanged
{
add
{
if (_valueChangedListeners == null)
{
_valueChangedListeners = new List<PropertyValueChangedHandler>();
}
_valueChangedListeners.Add(value);
if (_valueChangedListeners.Count == 1 && _target is INotifyPropertyChanged)
{
// Subscribe to value changed event on target.
((INotifyPropertyChanged)_target).PropertyChanged += OnTargetPropertyChanged;
}
}
remove
{
if (_valueChangedListeners == null)
{
return;
}
if (_valueChangedListeners.Remove(value) && _valueChangedListeners.Count == 0 &&
_target is INotifyPropertyChanged)
{
// Unsubscribe from value changed event on target.
((INotifyPropertyChanged) _target).PropertyChanged -= OnTargetPropertyChanged;
}
}
}
[CanBeNull] private readonly PropertyInfo _property;
[CanBeNull] private readonly object _target;
[CanBeNull] private readonly Attribute[] _attributes;
[CanBeNull] private readonly Func<object> _getter;
[CanBeNull] private readonly Action<object> _setter;
[CanBeNull] private List<PropertyValueChangedHandler> _valueChangedListeners;
public static PropertyReference FromLambda<T>(Func<T> getter, Action<T> setter = null, params Attribute[] attributes)
{
Action<object> internalSetter = null;
if (setter != null)
{
internalSetter = o => setter((T)o);
}
return new PropertyReference(typeof(T), () => getter(), internalSetter, attributes);
}
/// <summary>
/// Create a property reference from an object target and reflection PropertyInfo.
/// This represents a property on an object.
/// </summary>
public PropertyReference(object target, PropertyInfo property)
{
SRDebugUtil.AssertNotNull(target);
SRDebugUtil.AssertNotNull(property);
PropertyType = property.PropertyType;
_property = property;
_target = target;
#if NETFX_CORE
if(_property.GetMethod != null && _property.GetMethod.IsPublic)
#else
if (property.GetGetMethod() != null)
#endif
{
_getter = () => SRReflection.GetPropertyValue(target, property);
}
#if NETFX_CORE
if(_property.SetMethod != null && _property.SetMethod.IsPublic)
#else
if (property.GetSetMethod() != null)
#endif
{
_setter = (v) => SRReflection.SetPropertyValue(target, property, v);
}
}
/// <summary>
/// Create a property reference from lambdas. This has no underlying reflection or object associated with it.
/// </summary>
public PropertyReference(Type type, Func<object> getter = null, Action<object> setter = null, Attribute[] attributes = null)
{
SRDebugUtil.AssertNotNull(type);
PropertyType = type;
_attributes = attributes;
_getter = getter;
_setter = setter;
}
public Type PropertyType { get; private set; }
public bool CanRead
{
get
{
return _getter != null;
}
}
public bool CanWrite
{
get
{
return _setter != null;
}
}
/// <summary>
/// Notify any listeners to <see cref="ValueChanged"/> that the value has been updated.
/// </summary>
public void NotifyValueChanged()
{
if (_valueChangedListeners == null)
{
return;
}
foreach (var handler in _valueChangedListeners)
{
handler(this);
}
}
public object GetValue()
{
if (_getter != null)
{
return _getter();
}
return null;
}
public void SetValue(object value)
{
if (_setter != null)
{
_setter(value);
}
else
{
throw new InvalidOperationException("Can not write to property");
}
}
public T GetAttribute<T>() where T : Attribute
{
if (_attributes != null)
{
return _attributes.FirstOrDefault(p => p is T) as T;
}
if (_property != null)
{
return _property.GetCustomAttributes(typeof(T), true).FirstOrDefault() as T;
}
return null;
}
private void OnTargetPropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (_valueChangedListeners == null || _valueChangedListeners.Count == 0)
{
Debug.LogWarning("[PropertyReference] Received property value changed event when there are no listeners. Did the event not get unsubscribed correctly?");
return;
}
NotifyValueChanged();
}
}
}