创建基类方法的更好方法,该方法依赖于子类中的许多类型参数值

时间:2014-12-10 13:26:53

标签: c# asp.net .net oop

我有一个带有(很多)泛型类型参数的抽象基类HandlerProvider。这些参数用于确定(使用反射)从指定接口继承的类,并具有指定类型的自定义属性,该属性包含另一种类型的枚举值。 (所有类型都在类型参数中指定。)

接下来,我有两个实现抽象类的子类TriggerHandlerProvider和ActionHandlerProvider,并使用在抽象类中初始化的ClassTypes字典。

我的问题是:是否有更好(更优雅)的OOP方式来确定ClassTypes字典(除去至少一些类型参数)而不必复制每个子类中DetermineTypesToHandle的代码?

代码如下。

public interface IAttribute<out TEnum>
{
    TEnum GetValue();
}

public abstract class HandlerProvider<THandlerInterface, TInterface, TAttribute, TTypeEnum> : IHandlerProvider
    where TAttribute : Attribute, IAttribute<TTypeEnum>
{
    // Maps enum values to System.Type instances.
    protected readonly Dictionary<TTypeEnum, Type> ClassTypes;

    protected HandlerProvider(List<TTypeEnum> typeIds)
    {
        ClassTypes = new Dictionary<TTypeEnum, Type>();
        DetermineTypesToHandle(typeIds);
    }

    private void DetermineTypesToHandle(List<TTypeEnum> typeIds)
    {
        if (typeIds == null)
        {
            throw new ArgumentNullException();
        }
        IEnumerable<Type> classes = GetTypesWithAttribute(Assembly.GetExecutingAssembly());
        if (classes == null) return;
        foreach (Type classType in classes)
        {
            if (typeof(TInterface).IsAssignableFrom(classType))
            {
                TAttribute attribute = GetTypeAttribute(classType);
                TTypeEnum attributeValue = attribute != null ? attribute.GetValue() : default(TTypeEnum);
                if (!Equals(attributeValue, default(TTypeEnum)) && typeIds.Exists(tt => Equals(tt, attributeValue)))
                {
                    ClassTypes.Add(attributeValue, classType);
                }
            }
        }
    }

    private TAttribute GetTypeAttribute(Type classType)
    {
        return Attribute.GetCustomAttribute(classType, typeof(TAttribute)) as TAttribute;
    }

    private IEnumerable<Type> GetTypesWithAttribute(Assembly assembly)
    {
        return from t in assembly.GetTypes()
               where t.IsDefined(typeof(TAttribute), false)
               select t;
    }
}

public class TriggerHandlerProvider : HandlerProvider<ITriggerHandler, ITrigger, TriggerTypeAttribute, ETriggerType>
{
    public TriggerHandlerProvider(List<ETriggerType> typeIds)
        : base(typeIds)
    {
        // Use the ClassTypes property from the base class to create a new TriggerHandler.
    }
}

public class ActionHandlerProvider : HandlerProvider<IActionHandler, IAction, ActionTypeAttribute, EActionType>
{
    public ActionHandlerProvider(List<EActionType> typeIds)
        : base(typeIds)
    {
        // Use the ClassTypes property from the base class to create a new ActionHandler.
    }
}

1 个答案:

答案 0 :(得分:1)

@Jeroen,我建议你简化。

使用以下模式,您将只需要包含特定处理程序代码的类的一个属性(对于枚举值)。

每个特定的处理程序必须实现一个接口,即它与&#34;更高级别的接口&#34;处理程序提供程序(例如,您的ActionHandlerProvider)。

我把代码放在一个静态类中填充处理程序集合,因为这对我来说似乎更自然,但很容易让它成为一个基类&#34;。在任何情况下,您只需要两个参数,一个模板和一个接口。

接口可以保存可以以标准方式传递给特定处理程序的常用方法和数据,因此处理程序提供程序不必担心每个处理程序的特性(无论如何都是在modt的情况下)

Bellow是带有示例的代码。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Reflection;

namespace ConsoleApplication3
{
    [System.AttributeUsage(System.AttributeTargets.Class)]
    public class HandlerAttribute : Attribute
    {
        protected string fType;

        public HandlerAttribute(string type)
        {
            fType = type;
        }

        public string HandlerType
        {
            get { return fType; }
            set { fType = value; }
        }
    }

    public interface IHandlerA
    {
        //commom interface for handlers of type HandlerA
        string Name { get; }
    }

    public enum EHandlerATypes
    {
        A,
        B
    }

    [HandlerAttribute("A")]
    public class SpecificHandlerATypeA : IHandlerA
    {

        public string Name
        {
            get { return "HandlerA type A"; }
        }
    }

    [HandlerAttribute("B")]
    public class SpecificHandlerATypeB : IHandlerA
    {

        public string Name
        {
            get { return "HandlerA type B"; }
        }
    }

    public class HandlerA
    {
        public Dictionary<EHandlerATypes, Type> fHandlerACollection;

        public HandlerA()
        {
            fHandlerACollection = HandlerSearchEngine.GetHandlersList<EHandlerATypes, IHandlerA>(new Assembly[] { this.GetType().Assembly });
        }
    }

    public static class HandlerSearchEngine
    {
        public static Dictionary<TEnum, Type> GetHandlersList<TEnum, THandler>(Assembly[] assemblyList)
        {
            if (!typeof(TEnum).IsEnum)
                throw new Exception("Invalid parameter TEnum");

            Dictionary<TEnum, Type> dic = new Dictionary<TEnum, Type>();

            foreach(Assembly assembly in assemblyList)
            {
                var types = assembly.GetTypes().Where(t => t.IsClass && typeof(THandler).IsAssignableFrom(t));
                foreach(Type type in types)
                {
                    HandlerAttribute ha = type.GetCustomAttribute<HandlerAttribute>();
                    TEnum handlerType = (TEnum) Enum.Parse(typeof(TEnum), ha.HandlerType, true);

                    if (dic.ContainsKey(handlerType))
                        throw new Exception("One handler with the same handler type already exists in the collection");

                    dic[handlerType] = type;
                }
            }

            return dic;
        }
    }


    class Program
    {
        static void Main(string[] args)
        {
            HandlerA a = new HandlerA();
            foreach(KeyValuePair<EHandlerATypes, Type> pair in a.fHandlerACollection)
            {
                IHandlerA ha = (IHandlerA)Activator.CreateInstance(pair.Value);
                Console.WriteLine(ha.Name);
            }

            Console.ReadKey();
        }
    }
}

此示例的输出将为:

HandlerA type A 
HandlerA type B

希望这有助于简化您的工作。