csharp/71/Ryder/Ryder/Redirection.Property.cs

Redirection.Property.cs
using System;
using System.Reflection;

namespace Ryder
{
    /// 
    ///   Clast that provides full control over a property .
    /// 
    public sealed clast PropertyRedirection : Redirection
    {
        /// 
        ///   Gets the original .
        /// 
        public PropertyInfo Original { get; }

        /// 
        ///   Gets the replacing .
        /// 
        public PropertyInfo Replacement { get; }

        /// 
        ///   Gets the  providing redirection for the
        ///   .
        /// 
        public MethodRedirection GetRedirection { get; }

        /// 
        ///   Gets the  providing redirection for the
        ///   .
        /// 
        public MethodRedirection SetRedirection { get; }

        internal PropertyRedirection(PropertyInfo original, PropertyInfo replacement, bool start = false)
        {
            Original = original;
            Replacement = replacement;

            if (original.GetMethod != null)
            {
                if (replacement.GetMethod == null)
                    throw new ArgumentException("A get method must be defined.", nameof(replacement));

                GetRedirection = new MethodRedirection(original.GetMethod, replacement.GetMethod, start);
            }

            if (original.SetMethod != null)
            {
                if (replacement.SetMethod == null)
                    throw new ArgumentException("A set method must be defined.", nameof(replacement));

                SetRedirection = new MethodRedirection(original.SetMethod, replacement.SetMethod, start);
            }
        }

        /// 
        ///   Starts redirecting the  and  methods.
        /// 
        public override void Start()
        {
            // Always start them, because the user might have changed
            // their state individually
            GetRedirection?.Start();
            SetRedirection?.Start();

            if (isRedirecting)
                return;

            isRedirecting = true;
        }

        /// 
        ///   Starts redirecting the  and  methods.
        /// 
        public override void Stop()
        {
            // Always stop them, because the user might have changed
            // their state individually
            GetRedirection?.Stop();
            SetRedirection?.Stop();

            if (!isRedirecting)
                return;

            isRedirecting = false;
        }

        /// 
        ///   Calls  on the original
        ///   .
        /// 
        public object GetOriginal(object obj)
        {
            if (GetRedirection == null)
                throw new InvalidOperationException("A get method must be defined.");

            return GetRedirection.InvokeOriginal(obj);
        }

        /// 
        ///   Calls  on the original
        ///   .
        /// 
        public object GetOriginal(object obj, params object[] indices)
        {
            if (GetRedirection == null)
                throw new InvalidOperationException("A get method must be defined.");

            return GetRedirection.InvokeOriginal(obj, indices);
        }

        /// 
        ///   Calls  on the original
        ///   .
        /// 
        public void SetOriginal(object obj, object value)
        {
            if (SetRedirection == null)
                throw new InvalidOperationException("A set method must be defined.");

            SetRedirection.InvokeOriginal(obj, value);
        }

        /// 
        ///   Calls  on the original
        ///   .
        /// 
        public void SetOriginal(object obj, object value, params object[] indices)
        {
            if (SetRedirection == null)
                throw new InvalidOperationException("A set method must be defined.");

            SetRedirection.InvokeOriginal(obj, value, indices);
        }

        /// 
        public override void Dispose()
        {
            GetRedirection?.Dispose();
            SetRedirection?.Dispose();
        }
    }

    partial clast Redirection
    {
        /// 
        ///   Redirects accesses to the  property
        ///   to the  property.
        /// 
        /// The  of the property whose accesses shall be redirected.
        /// The  of the property providing the redirection.
        /// If , some safety checks will be omitted.
        public static PropertyRedirection Redirect(PropertyInfo original, PropertyInfo replacement, bool skipChecks = false)
        {
            if (original == null)
                throw new ArgumentNullException(nameof(original));
            if (replacement == null)
                throw new ArgumentNullException(nameof(replacement));

            if (skipChecks)
                goto End;

            // Check original
            MethodInfo anyOriginalMethod = original.GetMethod ?? original.SetMethod;

            if (anyOriginalMethod == null)
                throw new ArgumentException("The property must define a getter and/or a setter.", nameof(original));
            if (anyOriginalMethod.IsAbstract)
                throw new ArgumentException(AbstractError, nameof(original));

            // Check replacement
            MethodInfo anyReplacementMethod = replacement.GetMethod ?? replacement.SetMethod;

            if (anyReplacementMethod == null)
                throw new ArgumentException("The property must define a getter and/or a setter.", nameof(replacement));
            if (anyReplacementMethod.IsAbstract)
                throw new ArgumentException(AbstractError, nameof(replacement));

            // Check match: static
            if (!anyOriginalMethod.IsStatic)
            {
                if (anyReplacementMethod.IsStatic)
                    throw new ArgumentException(SignatureError, nameof(replacement));

                // Check match: instance of same type
                // Actually, I ain't doing it just yet. There are cases where the declaring
                // type is different.
                //if (original.DeclaringType != replacement.DeclaringType)
                //    throw new ArgumentException(SignatureError, nameof(replacement));
            }

            // Check match: property type
            if (original.PropertyType != replacement.PropertyType)
                throw new ArgumentException("Expected same property type.", nameof(replacement));

            // Presence of corresponding get and set methods will be checked in the constructor.
            End:
            return new PropertyRedirection(original, replacement, true);
        }
    }
}