10

我有一个我试图关联到 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 次。无论出于何种原因,我的大脑只是缺少一些明显的东西。处理此问题的最佳方法是什么。

4

7 回答 7

5

只要 Dto 类与 AbstractDto 定义在同一个命名空间中,这个类就会做你想做的事(如果没有,你需要调整它):

给定以下枚举和类:

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);
    }
}
于 2013-07-24T13:52:35.453 回答
5

使用Activator.CreateInstance方法并将枚举ToString值传递给它。

Type type = Type.GetType(DtoSelection.dto1.ToString());
var temp = Activator.CreateInstance(type);
于 2013-07-24T13:48:36.273 回答
2

我会使用功能字典。

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

var dto = dictionary[DtoSelection.dto1]();
于 2013-07-24T13:50:48.667 回答
2

您应该使用 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()约束(无参数构造函数的要求)。构造函数注入允许注册将用作带有参数的构造函数的参数的值。该参数可以是其他对象或抽象对象(接口、抽象类)。为此,您需要在 contianer 中注册此参数。

无论您选择哪种 IoC,都将比“穷人的 IoC”具有很多优势。

更新

如果你想避免多次写,大多数 IoC 容器也允许按名称注册,所以你可以这样注册:

  // 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());
  }

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

于 2013-07-24T14:00:48.267 回答
2

解决此问题的一种优雅方法是使用属性和一个基类。我来给你展示:

  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继承的每个类,这些类由您定义的 Enum 排序。您可以在 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 => ((DtoEnumAttribute)x.GetType().GetCustomAttributes(typeof(DtoEnumAttribute), false).FirstOrDefault()).Enum);
    
            //If you have parameters on you Dto's, you might pass them to CreateInstance(t, params)
    
     }
    

dtos列表中,您将拥有所需的实例。希望能帮助到你!

于 2020-02-28T15:38:10.720 回答
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() + ";");
    }
}
于 2013-07-24T13:52:32.000 回答
0
public AbstractDto CreateDto(DtoSelection selection)
{
    return (AbstractDto)Activator.CreateInstance(Type.GetType("Perhaps.Some.Qualifier.Here." + selection.ToString()));
}
于 2013-07-24T14:16:15.823 回答