private static void ValidateDefaultValueCommon(object defaultValue, Type propertyType, string propertyName, ValidateValueCallback validateValueCallback, bool checkThreadAffinity) { if (!IsValidType(defaultValue, propertyType)) { throw new ArgumentException(SR.Get("DefaultValuePropertyTypeMismatch", propertyName)); } if (defaultValue is Expression) { throw new ArgumentException(SR.Get("DefaultValueMayNotBeExpression")); } if (checkThreadAffinity) { DispatcherObject dispatcherObject = defaultValue as DispatcherObject; if (dispatcherObject != null && dispatcherObject.Dispatcher != null) { ISealable sealable = dispatcherObject as ISealable; if (sealable == null || !sealable.CanSeal) { throw new ArgumentException(SR.Get("DefaultValueMustBeFreeThreaded", propertyName)); } Invariant.Assert(!sealable.IsSealed, "A Sealed ISealable must not have dispatcher affinity"); sealable.Seal(); Invariant.Assert(dispatcherObject.Dispatcher == null, "ISealable.Seal() failed after ISealable.CanSeal returned true"); } } if (validateValueCallback != null && !validateValueCallback(defaultValue)) { throw new ArgumentException(SR.Get("DefaultValueInvalid", propertyName)); } }
public static void ThrowWhenSeal(this ISealable sealable, string errorMessage) { if (sealable.IsSealed) { throw new InvalidOperationException(errorMessage); } }
/// <summary> /// Recursively seal an object /// </summary> /// <param name="o">object to be recursively sealed</param> public void SealRecursively(object o) { // stop when we detect re-entry (reference loop) if (o == null || processStack.Contains(o)) { return; } // stops if o is not ISealable and we are not in deeper mode if (!(deeper || o is ISealable)) { return; } //if (isDebug) s_log.Debug("DeepSeal object: [" + o + "]"); // keep track of objects being processed, so we can detect re-entry processStack.Push(o); try { // we need to do generic collection first for two reasons: // 1. this is by contract in none deeper mode. see the // documentation of DealSeal methods for detail. // 2. if a dictionary implements both generic and non-generic, // foreach returns KeyValuePair instead of DictionaryEntry. bool isProcessed = TryProcessGenericCollection(o); // try to process as none generic collection if (!isProcessed) { isProcessed = TryProcessCollection(o); } // if o is not a collection, seal its all properties. if (!isProcessed) { DeepSealProperties(o); } // seal the object itself ISealable s = o as ISealable; if (s != null && !s.IsSealed) { s.Seal(); } } finally { processStack.Pop(); } }
/// <summary> /// Recursively deep seal an instance of <see cref="ISealable"/> object. /// </summary> /// <remarks> /// <para> /// <c>DeepSeal</c> walks through the collection items or object properties /// to find all <c>ISealable</c> objects. It recursively deep seals /// each <c>ISealable</c> it found. Then it seals the object itself. /// </para> /// <para> /// This methods only seals and recusively seals the <c>ISealable</c> object /// and its <c>ISealable</c> members. In other words, the recursively walks /// through elements of arrays, collections and properties, but the recursivity /// stops at any element that is not <c>ISealable</c>. /// <see cref="DeeperSeal"/> take one step /// further that it works on any object and seach for all its decending /// objects for <c>ISealable</c> objects and then seal them. /// </para> /// <para> /// Below is the pseudo code of deep sealing logic. Please note that the /// implementation can be very different as long as the same result is /// achieved. /// </para> /// <code> /// public static void DeepSeal(ISealalbe o) { /// if (o is re-entered) return; // handles the looped reference /// if (o is generic IDictionary(of TKey, TValue)) { /// foreach (pair in o) { /// if (TKey is assignable to ISealable) /// DeepSeal(pair.key); /// if (TValue is assignalbe to ISealable) /// DeepSeal(pair.value); /// } /// } else if (o is generic ICollection(of T) and T assingable to ISealable) { /// // this handles array as well, because array is a collection /// foreach (item in o) DeepSeal(item); /// } else if (o is non-generic IDictionary) { /// foreach (entry in o) { /// if (entry.key is ISealable) DeepSeal(entry.key); /// if (entry.value is ISealable) DeepSeal(entry.value); /// } /// } else if (o is non-generic collection) { /// foreach (item in o) { /// if (item is ISealable) DeepSeal(item); /// } /// } else { /// foreach (public property of o) { /// if (type of property is assignable to ISealable) DeepSeal(property); /// } /// } /// if (!o.isSealed) o.Seal() /// } /// </code> /// </remarks> /// <param name="o">the sealable to be deep sealed</param> /// <seealso cref="DeeperSeal"/> public static void DeepSeal(ISealable o) { new DeepSealer(false).SealRecursively(o); }
private static void ValidateDefaultValueCommon( object defaultValue, Type propertyType, string propertyName, ValidateValueCallback validateValueCallback, bool checkThreadAffinity) { // Ensure default value is the correct type if (!IsValidType(defaultValue, propertyType)) { throw new ArgumentException(SR.Format(SR.DefaultValuePropertyTypeMismatch, propertyName)); } // An Expression used as default value won't behave as expected since // it doesn't get evaluated. We explicitly fail it here. if (defaultValue is Expression) { throw new ArgumentException(SR.DefaultValueMayNotBeExpression); } if (checkThreadAffinity) { // If the default value is a DispatcherObject with thread affinity // we cannot accept it as a default value. If it implements ISealable // we attempt to seal it; if not we throw an exception. Types not // deriving from DispatcherObject are allowed - it is up to the user to // make any custom types free-threaded. DispatcherObject dispatcherObject = defaultValue as DispatcherObject; if (dispatcherObject != null && dispatcherObject.Dispatcher != null) { // Try to make the DispatcherObject free-threaded if it's an // ISealable. ISealable valueAsISealable = dispatcherObject as ISealable; if (valueAsISealable != null && valueAsISealable.CanSeal) { Invariant.Assert(!valueAsISealable.IsSealed, "A Sealed ISealable must not have dispatcher affinity"); valueAsISealable.Seal(); Invariant.Assert(dispatcherObject.Dispatcher == null, "ISealable.Seal() failed after ISealable.CanSeal returned true"); } else { throw new ArgumentException(SR.Format(SR.DefaultValueMustBeFreeThreaded, propertyName)); } } } // After checking for correct type, check default value against // validator (when one is given) if (validateValueCallback != null && !validateValueCallback(defaultValue)) { throw new ArgumentException(SR.Format(SR.DefaultValueInvalid, propertyName)); } }
public static void ThrowWhenSeal(this ISealable sealable) { sealable.ThrowWhenSeal(DefaultErrorMessage); }