C# 使用枚举选择要实例化的类
我有一个试图与dto关联的枚举:C# 使用枚举选择要实例化的类,c#,abstraction,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 } 我正在尝试创建一个方法(最终是一个服务),
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次。不管出于什么原因,我的大脑只是缺少了一些明显的东西。最好的处理方法是什么 使用方法并将其enum的传递给字符串
值
Type type = Type.GetType(DtoSelection.dto1.ToString());
var temp = Activator.CreateInstance(type);
我会使用funcs字典
Dictionary<DtoSelection, Func<AbstractDto>> dictionary =
new Dictionary<DtoSelection, Func<AbstractDto>>
{
{DtoSelection.dto1, () => new dto1()}
};
var dto = dictionary[DtoSelection.dto1]();
字典=
新词典
{
{DtoSelection.dto1,()=>newdto1()}
};
var dto=字典[DtoSelection.dto1]();
尝试使用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() + ";");
}
}
只要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);
}
}
您应该使用IoC容器(Unity、StructureMap、NINject…) 国际奥委会允许:
- 使用名称注册类型,如下所示(取决于容器):
注意:第一个参数是类型名(或完整类型名)。第二个是允许解析它的名称。解决这个问题的一种优雅方法是使用属性和一个基类。让我告诉你:public AbstractDto CreateDto(DtoSelection selection) { return (AbstractDto)Activator.CreateInstance(Type.GetType("Perhaps.Some.Qualifier.Here." + selection.ToString())); }
- 必须创建基类。在您的示例中,可以是AbstractDto,如下所示:
public abstract class AbstractDto : Attribute { //code of AbstractDto }
- 然后,我们需要创建一个自定义属性,该属性将用于每个Dto类,以确定哪个枚举对应于每个类
public class DtoEnumAttribute : Attribute { public DtoSelection Enum { get; set; } public DtoEnumAttribute(DtoSelection enum) { this.Enum = enum; } }
- 然后,我们应该用适当的枚举来装饰每个子Dto。让我们为Dto1做一个示例:
[DtoEnum(DtoSelection.Dto1)] public class Dto1 : AbstractDto { //code of Dto1 }
- 最后,您可以使用一个方法来接收特定的枚举和过滤器,或者任何您需要的逻辑。下面的代码将实例化从您定义的枚举排序的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 => ((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) }
public void MethodToGetInstances() { IEnumerable dtos=typeof(AbstractDto) .Assembly.GetTypes() 其中(t=>t.IsSubclassOf(typeof(AbstractDto))&&&!t.IsAbstract) .Select(t=>(AbstractDto)Activator.CreateInstance(t)) .OrderBy(x=>((DtoEnumAttribute)x.GetType().GetCustomAttributes(typeof(DtoEnumAttribute),false).FirstOrDefault()).Enum); //如果Dto上有参数,可以将它们传递给CreateInstance(t,params) }
在dtos列表中,您将拥有所需的实例。
希望有帮助 我很好奇这是否可行。首先,您可以使用
开关改进if
列表。你想使用反射吗?在一个枚举中有108个不同的dto对象背后的原因是什么?为什么不使用数组呢?除了一些脆弱的反射,您可以创建一个字典
,在启动时(比如在私有静态构造函数中)就可以用工厂方法填充它。然后就这样叫它myDict[(DtoSelection)id]()
@ChrisSinclair你如何在不“做108次”的情况下填充这本词典?我正在努力避免写108次。我需要构建一个所有类型的字典。对于我来说,避免使用丑陋的开关非常有用,我喜欢这样。不过,对于每个dto,我必须这样做108次。@Robert,您可以在枚举的成员上创建一个循环,然后创建实例并将其添加到列表中,对于循环,请参见:这是过度杀戮,依我看。@newStackExchangeInstance为什么过度杀戮?您只需添加一个Nuget包,并编写几行代码,比编写自己的“穷人的IoC”要少。此外,你要避免犯错误或忘记细节。它将比您几乎可以实现的任何东西都更安全、更高效地工作。对我来说,过度杀戮就是重新发明轮子来解决一个已经解决的问题。。。可能会引入一些意想不到的错误。让我们看看,更复杂的是什么(因此也有错误):Activator.CreateInstance还是一个复杂的IoC框架?有些错误不是因为它的复杂性,而是因为它的成熟度和质量。大多数IoC设计良好,有很多单元测试,多年来一直在LoB应用程序的生产中工作。这意味着它不是马车。当您使用Activator.CreateInstance时,什么会失败?你如何控制它?你还需要考虑什么?这一切都是在成熟的IoC容器中解决的。不要期望某个东西不是错误的,因为它是“一条无辜的指令”。在您的代码中,如果用户传递了错误的id会发生什么?它会抛出TypeNotFoundException。你的生活中发生了什么?
public abstract class AbstractDto : Attribute
{
//code of AbstractDto
}
public class DtoEnumAttribute : Attribute
{
public DtoSelection Enum { get; set; }
public DtoEnumAttribute(DtoSelection enum)
{
this.Enum = enum;
}
}
[DtoEnum(DtoSelection.Dto1)]
public class Dto1 : AbstractDto
{
//code of Dto1
}
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)
}