Andrey on .NET | Динамический декоратор. Часть 3 – Исходный код

Динамический декоратор. Часть 3 – Исходный код

Нет желания загружать еще один экземпляр Visual Studio или скачивать проект, а взглянуть на исходный код интересно? Тогда вот он:

Исходный код Dynamic Decorator

// DynamicDecorator
// <copyright file="DynamicDecorator.cs">
// Copyright (c) Andrey Veselov. All rights reserved.
// License: Apache License 2.0
// </copyright>
// <author>Andrey Veselov</author>
// Contact: http://andrey.moveax.ru/contact.aspx
namespace Patterns.Decorator
{
    using System;
    using System.Dynamic;
    using System.Reflection;
    using Castle.DynamicProxy;

    /// <summary>Dynamic implementation of the Decorator pattern.</summary>
    /// <typeparam name="TComponentInterface">The interface of the component.</typeparam>
    public class DynamicDecorator<TComponentInterface> : DynamicObject, IInterceptor
    {
        /// <summary>Component - defines an object to which 
        /// additional responsibilities can be attached.</summary>
        protected TComponentInterface _component;

        /// <summary>Represents component type.</summary>
        private Type _componentType;

        /// <summary>Initializes a new instance of the 
        /// <see cref="DynamicDecorator&lt;TComponentInterface&gt;"/> class.</summary>
        /// <param name="component">The component to which additional 
        /// responsibilities can be attached.</param>
        public DynamicDecorator(TComponentInterface component)
        {
            this.Component = component;

            var _generator = new ProxyGenerator();
            this.Interface = (TComponentInterface)_generator.CreateInterfaceProxyWithoutTarget(
                typeof(TComponentInterface), this);
        }

        /// <summary>Gets or sets the object to which additional 
        /// responsibilities can be attached.</summary>
        public TComponentInterface Component
        {
            get { return this._component; }
            set
            {
                this._component = value;
                this._componentType = this._component.GetType();
            }
        }

        /// <summary>Gets the component interface.</summary>
        /// <value>The dynamic proxy used to implement the TComponent interface.</value>
        public TComponentInterface Interface { get; private set; }

        #region DynamicObject overrides

        /// <summary>Provides the implementation for operations that set member values.</summary>
        /// <param name="binder">Provides information about the object 
        /// that called the dynamic operation.</param>
        /// <param name="value">The value to set to the member.</param>
        /// <returns>true if the operation is successful; otherwise, false.</returns>
        public override bool TrySetMember(SetMemberBinder binder, object value)
        {
            // search for a property
            PropertyInfo property = this._componentType.GetProperty(
                binder.Name, BindingFlags.Public | BindingFlags.Instance);
            if (property != null) {
                property.SetValue(this._component, value, null);
                return true;
            }

            // search for a public field
            FieldInfo field = this._componentType.GetField(
                binder.Name, BindingFlags.Public | BindingFlags.Instance);
            if (field != null) {
                field.SetValue(this._component, value);
                return true;
            }

            return base.TrySetMember(binder, value);
        }

        /// <summary>Provides the implementation for operations that get member values.</summary>
        /// <param name="binder">Provides information about the object
        /// that called the dynamic operation.</param>
        /// <param name="result">The result of the get operation.</param>
        /// <returns>true if the operation is successful; otherwise, false.</returns>
        public override bool TryGetMember(GetMemberBinder binder, out object result)
        {
            // search for a property
            PropertyInfo property = this._componentType.GetProperty(
                binder.Name, BindingFlags.Public | BindingFlags.Instance);
            if (property != null) {
                result = property.GetValue(this._component, null);
                return true;
            }

            // search for a public field
            FieldInfo field = this._componentType.GetField(
                binder.Name, BindingFlags.Public | BindingFlags.Instance);
            if (field != null) {
                result = field.GetValue(this._component);
                return true;
            }

            return base.TryGetMember(binder, out result);
        }

        /// <summary>Provides the implementation for operations that invoke a member.</summary>
        /// <param name="binder">Provides information about the dynamic operation.</param>
        /// <param name="args">The arguments that are passed to the object member 
        /// during the invoke operation.</param>
        /// <param name="result">The result of the member invocation.</param>
        /// <returns>true if the operation is successful; otherwise, false.</returns>
        public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args,
            out object result)
        {
            MethodInfo method = this._componentType.GetMethod(
                binder.Name, BindingFlags.Public | BindingFlags.Instance,
                null, this.GetArgumentsTypes(args), null);

            if (method != null) {
                result = method.Invoke(this._component, args);
                return true;
            }

            return base.TryInvokeMember(binder, args, out result);
        }

        #endregion

        #region Castle IInterceptor

        /// <summary>Intercepts the specified invocation.</summary>
        /// <param name="invocation">The invocation.</param>
        public void Intercept(IInvocation invocation)
        {
            Type decoratorType = this.GetType();
            Type[] argTypes = this.GetArgumentsTypes(invocation.Arguments);

            MethodInfo method = decoratorType.GetMethod(
                invocation.Method.Name, BindingFlags.Public | BindingFlags.Instance, 
                null, argTypes, null);

            if (method != null) {
                invocation.ReturnValue = method.Invoke(this, invocation.Arguments);
                return;
            }

            method = this._componentType.GetMethod(
                invocation.Method.Name, BindingFlags.Public | BindingFlags.Instance,
                null, argTypes, null);

            if (method != null) {
                invocation.ReturnValue = method.Invoke(this._component, invocation.Arguments);
                return;
            }

            // No match was found for required method
            string argList = this.GetArgumentsString(invocation.Arguments);
            throw new InvalidOperationException(
                string.Format("No match was found for method {0}({1}).",
                    invocation.Method.Name, argList));
        }

        #endregion

        /// <summary>Gets the component.</summary>
        /// <returns>The component.</returns>
        public TComponentInterface GetComponent()
        {
            var decorator = this.Component as DynamicDecorator<TComponentInterface>;
            if (decorator != null) {
                return decorator.GetComponent();
            }

            return this.Component;
        }

        /// <summary>Sets the component.</summary>
        /// <param name="component">The component to set.</param>
        public void SetComponent(TComponentInterface component)
        {
            var decorator = this.Component as DynamicDecorator<TComponentInterface>;
            if (decorator != null) {
                decorator.SetComponent(component);
                return;
            }

            this.Component = component;
        }

        /// <summary>Gets the list of arguments types.</summary>
        /// <param name="args">An object array that contains arguments.</param>
        /// <returns>The list of arguments types.</returns>
        private Type[] GetArgumentsTypes(object[] args)
        {
            Type[] argTypes = new Type[args.GetLength(0)];

            int index = 0;
            foreach (var arg in args) {
                argTypes[index] = arg.GetType();
                index++;
            }

            return argTypes;
        }

        /// <summary>Gets the list of arguments types as string.</summary>
        /// <param name="args">An object array that contains arguments.</param>
        /// <returns>The list of arguments types as string.</returns>
        private string GetArgumentsString(object[] args)
        {
            string argList = string.Empty;
            bool isFirstArgument = true;

            foreach (var arg in args) {
                if (isFirstArgument) {
                    isFirstArgument = false;
                }
                else {
                    argList += ", ";
                }

                argList += arg.GetType().ToString();
            }

            return argList;
        }
    }

    /// <summary>Dynamic implementation of the Decorator pattern.</summary>
    /// <typeparam name="TComponent">The type of the component.</typeparam>
    /// <typeparam name="TComponentInterface">The interface of the component.</typeparam>
    public class DynamicDecorator<TComponent, TComponentInterface> :
        DynamicDecorator<TComponentInterface>
        where TComponent : class, TComponentInterface, new()
    {
        /// <summary>Initializes a new instance of the 
        /// <see cref="DynamicDecorator&lt;TComponent, TComponentInterface&gt;"/>
        /// class.</summary>
        public DynamicDecorator() : base((TComponentInterface)new TComponent()) { }
    }
}

Исходный код примера использования

namespace DynamicDecoratorDemo
{
    using System;
    using Patterns.Decorator;

    public interface IElement
    {
        string Text { get; set; }

        void Draw();
    }

    public class Element : IElement
    {
        public string Text { get; set; }

        public void Draw()
        {
            Console.WriteLine("Drawing element ({0})", this.Text);
        }
    }

    public class ElementStrikedDecorator : DynamicDecorator<IElement>
    {
        public ElementStrikedDecorator(IElement component) : base(component) { }

        public void Draw()
        {
            this._component.Draw();
            this.Strike();
        }

        private void Strike()
        {
            Console.WriteLine("Striked");
        }
    }

    public class ElementBgndDecorator : DynamicDecorator<IElement>
    {
        public ElementBgndDecorator(IElement component) : base(component) { }

        public string BackgroundFileName { get; set; }

        public void Draw()
        {
            this.SetBackground();
            this._component.Draw();
        }

        private void SetBackground()
        {
            Console.WriteLine("Background: {0}", this.BackgroundFileName);
        }
    }

    public class MyDynamic : System.Dynamic.IDynamicMetaObjectProvider
    {
        public void DefinedMethod()
        {
            Console.WriteLine("DefinedMethod");
        }

        public System.Dynamic.DynamicMetaObject GetMetaObject(
            System.Linq.Expressions.Expression parameter)
        {
            throw new NotImplementedException();
        }
    }

    class Program
    {
        public static void DrawElement(string title, IElement element)
        {
            Console.WriteLine("{0} ---------------------------", title);
            element.Draw();
            Console.WriteLine(string.Empty);
        }

        static void Main(string[] args)
        {

            //dynamic myDynamicObject = new MyDynamic();
            //myDynamicObject.DefinedMethod();
            //myDynamicObject.Test();

            Element element = new Element();
            DrawElement("Base element", element);

            dynamic elementBgnd = new ElementBgndDecorator(element);
            elementBgnd.BackgroundFileName = "SomeBgndFile.png";

            dynamic elementStriked = new ElementStrikedDecorator(elementBgnd.Interface);
            elementStriked.Text = "Demo";

            DrawElement("Striked element with background", elementStriked.Interface);

            IElement sourceElement = elementStriked.GetComponent();
            DrawElement("Element with background", sourceElement);

            Console.WriteLine("Press any key ...");
            Console.ReadKey();
        }
    }
}

Не забудьте подключить к проекту сборку Castle Project Core.

Ну и еще раз ссылка на демонстрационный проект для Visual Studio 2010:
DynamicDecorator.zip (141 kb).

Комментарии закрыты