2

我有几个基于标准抽象类的类,它们将从数据表中加载(对于示例,我使用了一个 int 数组)。该类将按类型启动,然后加载该类的特定详细信息。目前我正在使用 switch 语句执行此操作,但是否可以通过其他方式执行此操作?

class Program
{
    static void Main(string[] args)
    {
        var list = new[] {1, 1, 3, 2, 2, 4};
        TypeBase typeClass = null;
        foreach (var i in list)
        {
            switch (i)
            {
                case 1:
                    {
                        typeClass = new Type1();
                        break;
                    }
                case 2:
                    {
                        typeClass = new Type2();
                        break;
                    }
                case 3:
                    {
                        typeClass = new Type3();
                        break;
                    }
            }
        }
        if (typeClass != null)
        {
            typeClass.LoadDetails();
        }
    }
}

public class TypeBase
{
    public int Type { get; set; }
    public virtual void LoadDetails()
    {
        throw new NotImplementedException();
    }
}

public class Type1 : TypeBase
{
    public override void LoadDetails()
    {
        // Load type Specific details
    }
}

public class Type2 : TypeBase
{
    public override void LoadDetails()
    {
        // Load type Specific details
    }
}

public class Type3 : TypeBase
{
    public override void LoadDetails()
    {
        // Load type Specific details
    }
}
4

2 回答 2

3

有几种解决方案。

1. 使用反射创建类型

Nair 已经给出了这个解决方案。由于我希望您的类型不会被称为Type1,Type2等。我认为这对您不起作用。

2.将类型存储在带有类型的字典中

创建一个将替换您的开关/案例的字典。它包含您需要创建的类型:

Dictionary<int, Type> types = new Dictionary<int, Type>
{
    {1, typeof(Type1)},
    {2, typeof(Type2)},
    {3, typeof(Type3)}
};

使用它来创建您的类型:

Type type;
if (types.TryGetValue(i, out type))
{
    TypeBase typeClass = (TypeBase)Activator.CreateInstance(type);
    typeClass.LoadDetails();
}

该解决方案比解决方案#1 更快,因为只使用了一个“反射”操作。

3. 使用工厂方法将类型存储在字典中

创建一个将替换您的开关/案例的字典。它将包含工厂方法:

Dictionary<int, Func<TypeBase>> factories = new Dictionary<int, Func<TypeBase>>
{
    {1, () => new Type1()},
    {2, () => new Type2()},
    {3, () => new Type3()}
};

使用它来创建您的类型:

Func<TypeBase> factory;
if (factories.TryGetValue(i, out factory))
{
    TypeBase typeClass = factory();
    typeClass.LoadDetails();
}

该解决方案比解决方案#2 更快,因为没有使用反射。

4. 将类型存储在带有实例的字典中

创建一个将替换您的开关/案例的字典。它将包含您的类型的实例。此解决方案仅在这些实例是不可变的并且在调用期间不会更改状态时才有效。

Dictionary<int, TypeBase> typeClasses = new Dictionary<int, TypeBase>
{
    {1, new Type1()},
    {2, new Type2()},
    {3, new Type3()}
};

使用它来创建您的类型:

TypeBase typeClass;
if (baseClasses.TryGetValue(i, out baseClass))
{
    typeClass.LoadDetails();  
}

此解决方案比解决方案 #3 更快,因为每次调用都不会创建实例。

一些旁注

  • 为什么不使用一个接口,只有一个成员LoadDetails?在您的示例Type中,成员从未使用过。
  • 为什么不做TypeBase.LoadDetails一个抽象方法?
  • 如果您keys始终是类型Int32并且处于连续范围内,则可以考虑使用List<T>数组甚至数组,这将比字典更快。
于 2013-05-09T09:42:37.540 回答
1

理想情况下,您应该遵循工厂方法来满足此类要求。否则,如果您乐于遵循子类的约定,那么一个便宜的技巧将如下所示。

using System;
using System.Runtime.Remoting;

namespace ConsoleApplication1
{
    internal class Program
    {
        private static void Main(string[] args)
        {
            var list = new[] {1, 1, 3, 2, 2, 4};
            TypeBase typeClass = null;
            foreach (var i in list)
            {
                ObjectHandle handle = Activator.CreateInstanceFrom("ConsoleApplication1", string.Format("{0}{1}", "Type", i));//Program- Name of the assembl
                var typeBase = (TypeBase) handle.Unwrap();
                typeBase.Type = i;
                typeClass.LoadDetails();
            }
        }
    }

    public class TypeBase
    {
        public int Type { get; set; }

        public virtual void LoadDetails()
        {
            throw new NotImplementedException();
        }
    }

    public class Type1 : TypeBase
    {
        public override void LoadDetails()
        {
            // Load type Specific details
        }
    }
}

注意: 我个人不会采用这种方法,并且对工厂方法或相当数量的开关更满意。这只是闪现一种可能而已。请进行相应的测试和修改(如果决定遵循)

于 2013-05-07T15:17:36.773 回答