使用枚举来选择要实例化的类

时间:2013-07-24 13:44:17

标签: c# abstraction

我有一个我想尝试与dto联系的枚举:

 public enum DtoSelection
 {
     dto1,
     dto2,
     dto3,
 }

此枚举中有108个值。

我为这些dto中的每一个都有一个dto对象:

 public class dto1 : AbstractDto
 {
       public int Id { get; set; }
       //some stuff specific to this dto
 }

我正在尝试创建一个方法(最终是一个服务),它将返回一个与所讨论的dto相关联的类型的新dto对象:

 private AbstractDto(int id)
 {
      if (id == DtoSelection.Dto1.ToInt()) //extension method I wrote for enums
            return new Dto1();
      if (id == DtoSelection.Dto2.ToInt())
            return new Dto2();
 }

显然我不想这样做108次。无论出于何种原因,我的大脑只是遗漏了一些明显处理这个问题的最佳方法是什么。

7 个答案:

答案 0 :(得分:4)

使用Activator.CreateInstance方法并将其传递给enum的ToString值。

Type type = Type.GetType(DtoSelection.dto1.ToString());
var temp = Activator.CreateInstance(type);

答案 1 :(得分:4)

只要在与AbstractDto相同的命名空间中定义Dto类,此类就可以执行您想要的操作(如果没有,则需要调整它):

给出以下枚举和类:

public enum DtoSelection
{
    Dto1,
    Dto2,
    Dto3,
}

public abstract class AbstractDto
{
}

public class Dto1 : AbstractDto
{
}

public class Dto2 : AbstractDto
{
}

public class Dto3 : AbstractDto
{
}

此方法将解决它们:

public static class DtoFactory
{
    public static AbstractDto Create(DtoSelection dtoSelection)
    {
        var type = Type.GetType(typeof(AbstractDto).Namespace + "." + dtoSelection.ToString(), throwOnError: false);

        if (type == null)
        {
            throw new InvalidOperationException(dtoSelection.ToString() + " is not a known dto type");
        }

        if (!typeof(AbstractDto).IsAssignableFrom(type))
        {
            throw new InvalidOperationException(type.Name + " does not inherit from AbstractDto");
        }

        return (AbstractDto)Activator.CreateInstance(type);
    }
}

答案 2 :(得分:1)

我会使用funcs字典。

Dictionary<DtoSelection, Func<AbstractDto>> dictionary = 
        new Dictionary<DtoSelection, Func<AbstractDto>>
{
    {DtoSelection.dto1, () => new dto1()}
};

var dto = dictionary[DtoSelection.dto1]();

答案 3 :(得分:1)

尝试使用Activator.CreateInstance

return (AbstractDto)Activator.CreateInstance
                        (Type.GetType(((DtoSelection)id).ToString(), true, true);

或者,有点作弊,你可以使用一些代码生成:

public static string GenerateValues()
{
    StringBuilder sb = new StringBuilder();
    sb.AppendLine("DtoSelection selection = (DtoSelection)id;");
    sb.AppendLine("switch (selection)");
    foreach (DtoSelection value in (DtoSelection[])Enum.GetValues(typeof(DtoSelection))
    {
        sb.AppendLine("case DtoSelection." + value.ToString() + ":");
        sb.AppendLine("return new " + value.ToString() + ";");
    }
}

答案 4 :(得分:1)

解决此问题的一种优雅方法是使用Attributes和一个基类。让我告诉你:

  1. 您必须创建一个基类。在您的示例中,可以是AbstractDto,如下所示:

    public abstract class AbstractDto : Attribute
    {
         //code of AbstractDto       
    }
    
  2. 然后,我们需要创建一个自定义属性,该属性将用于每个Dto类,以确定与每个类对应的枚举。

    public class DtoEnumAttribute : Attribute
    {
        public DtoSelection Enum { get; set; }
    
        public DtoEnumAttribute(DtoSelection enum)
        {
            this.Enum = enum;
        }
     }
    
  3. 然后,我们应该用适当的枚举装饰每个子Dto。让我们为Dto1做一个例子:

    [DtoEnum(DtoSelection.Dto1)]
    public class Dto1 : AbstractDto
    {
         //code of Dto1
    }
    
  4. 最后,您可以使用一种方法,该方法可以接收特定的枚举和过滤器,或者您需要的任何逻辑。以下代码将实例化从您定义的枚举排序的 AbstractDto 继承的每个类。您可以在Where子句上使用它,以仅返回与所需枚举匹配的类的实例。问我在这一点上是否需要帮助。

    public void MethodToGetInstances()
    {
           IEnumerable<AbstractDto> dtos = typeof(AbstractDto)
               .Assembly.GetTypes()
               .Where(t => t.IsSubclassOf(typeof(AbstractDto)) && !t.IsAbstract)
               .Select(t => (AbstractDto)Activator.CreateInstance(t))
               .OrderBy(x => ((EnumDtoAttribute)x.GetType().GetCustomAttributes(typeof(EnumDtoAttribute), false).FirstOrDefault()).Enum);
    
           //If you have parameters on you Dto's, you might pass them to CreateInstance(t, params)
    
    }
    

dtos 列表上,您将拥有所需的实例。 希望对您有帮助!

答案 5 :(得分:0)

您应该使用IoC容器(Unity,StructureMap,NINject ...)

Ioc允许:

  • 使用名称注册一个类型,如下所示(取决于容器):

    Container.Register<AbstractDto,Dto1>(DtoSelection.dto1.ToString());
    
  • 解析类型

    Container.Resolve<AbstractDto>(DtoSelection.dto1.ToString());
    

这将为您处理实例化的所有细节。

提供的其他解决方案被称为“穷人的IoC”。不要重新发明轮子。

当然,你应该隐藏方法背后的容器:

  public void RegisterDto<TDto>(DtoSelection dtoSelection)
    where TDto : AbstractDto, new()
  {
     Container.Register<AbstractDto,Dto1>(dtoSelection.ToString());
  }


  public TDto GetDto<TDto>(DtoSelection dtoSelection)
    where TDto : AbstractDto
  {
     return Container.Resolve<AbstractDto>(dtoSelection.ToString()) as TDto;
  }

注意:如果使用“构造函数注入”,则可以删除new()约束(requirement of parameterless constructor)。构造函数注入允许注册将用作带参数的构造函数的参数的值。此参数可以是其他对象或抽象对象(接口,abstrac类)。为此,您需要在contianer中注册此参数。

无论你选择哪种IoC都会比“穷人的IoC”有很多优势。

<强>更新

如果您想避免多次写入,大多数IoC COntainer也允许按名称注册,因此您可以进行如下注册:

  // iterate the DtoSelection Enum
  foreach(var e in Enum.GetValues(DtoSelection))
  {
    DtoSelection dtoSel = (DtoSelection)e;
    int n = (int)dtoSel;
    Container.Register<AbstractDto>("Dto" + n, dtoSel.ToString());
  }

注意:第一个参数是类型名称(或完整类型名称)。第二个是允许解决它的名称。

答案 6 :(得分:0)

public AbstractDto CreateDto(DtoSelection selection)
{
    return (AbstractDto)Activator.CreateInstance(Type.GetType("Perhaps.Some.Qualifier.Here." + selection.ToString()));
}