C#如何优雅封装通用方法创建不同类实例的List集合
问题描述
我是编程领域的新手,因工作需要入门学习开发技能。我编写了MyLists类,专门用于创建和存储由不同类对象构成的各类List集合,但我认为当前实现违反了DRY原则。
初始冗余实现
最开始的字段定义代码如下:
public class MyLists { private List<Apple> ListedApples = new List<Apple>(); private List<Orange> ListedOranges = new List<Orange>(); }
我为每一类对象都单独编写了对应的列表生成方法,代码如下:
public class MyLists { public List<Apple> AppleLister(int howMany) { List<Apple> appleList = new List<Apple>(); for (int i = 0; i < howMany; i++) { Apple apple = new Apple(); appleList.Add(apple); } ListedApples = appleList; return ListedApples; } public List<Orange> OrangeLister(int howMany) { List<Orange> orangeList = new List<Orange>(); for (int i = 0; i < howMany; i++) { Orange orange = new Orange(); orangeList.Add(orange); } ListedOranges = orangeList; return ListedOranges; } }
上述实现冗余度高、可维护性差,我尝试使用泛型编写适配任意类的通用方法,但代码无法正常运行:
public static void Lister<T>(List<T> list, int howMany) where T : class { new List<T>(); for (int i = 0; i < howMany; i++) { var instance = Activator.CreateInstance(typeof(T), new object()); list.Add(instance); } return List<T>(); }
我了解到C#存在类型转换等适配方案,但业界普遍不建议做这类类型混用操作。我想咨询当前场景下通用列表生成方法的规范化封装方式,后续我还计划向生成的对象中添加位置等自定义字段。
解决方案
你写的泛型方法跑不起来是基础语法和逻辑错误导致的,泛型本身就是C#里解决这类重复代码场景的标准方案,完全不需要做危险的类型混用。
原有泛型代码的问题
你原来的代码存在4个明显错误:
- 方法声明返回值是
void,最后却写了return List<T>(),语法不匹配 - 方法里写了
new List<T>()但没有赋值给任何变量,属于无效代码 Activator.CreateInstance传参错误:你给无参构造传了一个new object()参数,会触发找不到匹配构造函数的异常,无参构造直接不传第二个参数即可- 你把外部传入的list作为容器添加元素,又没有把list存回类的字段,和原有逻辑不一致
推荐实现方案
方案1:泛型+无参构造约束(最简洁,适配当前基础场景)
给泛型加new()约束,就可以直接在泛型方法里new T(),比Activator性能更好、是编译期类型安全的,不需要反射:
public class MyLists { // 用字典统一存储所有类型的列表,不用为每个类型单独写字段 private readonly Dictionary<Type, object> _storage = new Dictionary<Type, object>(); public List<T> Lister<T>(int howMany) where T : class, new() { var resultList = new List<T>(howMany); for (int i = 0; i < howMany; i++) { T instance = new T(); // 后续要加位置等字段,直接在这里给instance赋值即可 resultList.Add(instance); } _storage[typeof(T)] = resultList; return resultList; } // 需要取已生成的列表时加这个辅助方法即可 public List<T> GetList<T>() where T : class, new() { return _storage.TryGetValue(typeof(T), out var list) ? (List<T>)list : Enumerable.Empty<T>().ToList(); } }
调用方式非常简单,不需要为每个类型写单独方法:
var myLists = new MyLists(); List<Apple> apples = myLists.Lister<Apple>(10); // 生成10个Apple的列表 List<Orange> oranges = myLists.Lister<Orange>(5); // 生成5个Orange的列表
方案2:工厂委托模式(适配后续自定义字段初始化需求)
如果后续初始化每个对象的时候需要传入不同的参数(比如你要加的位置字段,每个对象的位置值不同),可以传入初始化委托,灵活性更高,完全没有类型转换风险:
public List<T> Lister<T>(int howMany, Func<int, T> instanceFactory) { var resultList = new List<T>(howMany); for (int i = 0; i < howMany; i++) { // 传入的委托可以自定义每个对象的初始化逻辑,i是当前生成的序号 T instance = instanceFactory(i); resultList.Add(instance); } _storage[typeof(T)] = resultList; return resultList; }
调用时可以直接自定义初始化逻辑,完美适配后续加位置字段的需求:
// 生成10个Apple,每个Apple的Position属性按序号赋值 var apples = myLists.Lister<Apple>(10, index => new Apple { Position = new Point(index * 10, 0) });
实践注意事项
- 尽量不要用
Activator.CreateInstance做无参构造的实例化,new()泛型约束是编译期检查的,不会出现运行时找不到构造函数的问题,性能也更好 - 用字典统一存储不同类型的列表,就不需要为每个新增的实体类单独加私有字段,扩展性更强
- 传入工厂委托的方案所有类型检查都是编译期完成的,不存在类型混用的风险,是工业界开发这类通用逻辑的标准写法
内容的提问来源于stack exchange,提问作者JCRG
相关产品推荐
相关产品推荐

