using System; using System.Collections.Generic; using System.Linq; using System.Text; using Chernobyl.Event; using Chernobyl.Utility; namespace Chernobyl.Values { /// /// A basic implementation of an intended to make /// the creation of easier. /// /// The type held by . public abstract class BasicValue : IValue { /// /// Initializes a new instance of the class. /// protected BasicValue() : this(EqualityComparer.Default) {} /// /// Initializes a new instance of the class. /// /// The instance used to compare new and previous /// values of to determine if /// should be raised. protected BasicValue(IEqualityComparer comparer) { Comparer = comparer; } /// /// An event that is raised immediately after the value held by this /// instance has been modified. /// public event EventHandler> Provide { add { ProvideHandler += value; if (IsReady) value(this, new ValueChangedEventArgs(PreviousValue, Value)); IsNeeded = true; } remove { ProvideHandler -= value; if(ProvideHandler.GetInvocationList().Length == 0) IsNeeded = false; } } /// /// The value held by this instance. If the value has not yet been /// provided it will be equal to default(T). Setting this property /// will make this instance ready. /// protected T Value { get { // Don't set IsNeeded to true since this property is protected // and the only instances accessing the get of this property will // be types who have control over readiness. return BackingValue; } set { if (IsReady == false || Comparer.Equals(BackingValue, value) == false) { PreviousValue = BackingValue; BackingValue = value; // Setting IsReady to true when it was previously false will // raise Provide. Otherwise, raise Provide since setting // IsReady to true when it is already true will not raise // Provide. if (IsReady == false) IsReady = true; else if (ProvideHandler != null) ProvideHandler(this, new ValueChangedEventArgs(PreviousValue, Value)); } } } /// /// The backing property or implementation to . This /// property should just set and get the value. The /// property handles setting of and /// and the raising of /// . /// protected abstract T BackingValue { get; set; } /// /// The previous value of . /// protected abstract T PreviousValue { get; set; } /// /// The instance used to compare new and previous values of /// to determine if should be /// raised. /// /// Thrown if the value set on /// this property is null. public IEqualityComparer Comparer { get { return _comparer; } protected set { value.ThrowIfNull("value"); _comparer = value; } } /// /// True if event handlers set on should be /// invoked with the current value, false if otherwise. Setting this /// property to true when it was previously false will cause the /// event to be raised. This property is made true /// when the is set. By default this property is false. /// protected bool IsReady { get { return _isReady; } set { if (_isReady != value) { _isReady = value; if (_isReady && ProvideHandler != null) ProvideHandler(this, new ValueChangedEventArgs(PreviousValue, Value)); } } } /// /// Set to true when client code subscribes to the /// event. /// protected bool IsNeeded { get { return _isNeeded; } set { if (_isNeeded != value) { _isNeeded = value; if (_isNeeded) Needed(); else NotNeeded(); } } } /// /// Invoked when is changed to true. /// Implementations of this method should invoke the base class method. /// protected virtual void Needed() { } /// /// Invoked when is changed to false. /// Implementations of this method should invoke the base class method. /// protected virtual void NotNeeded() { } /// /// The backing field to . /// protected EventHandler> ProvideHandler { get; private set; } /// /// The backing field to . /// IEqualityComparer _comparer; /// /// The backing field to . /// bool _isReady; /// /// The backing field to . /// bool _isNeeded; } }