C#泛型单例工厂优化问询:线程安全、销毁及带参构造
C#泛型单例工厂的优化方案与常见问题解答
1. 线程安全优化
你的原始实现中Dictionary<TKey,TValue>不是线程安全的,多线程并发调用GetInstance<T>()时会出现竞态条件(比如多个线程同时检测到类型不存在,重复创建实例)。以下是两种可靠的优化方案:
方案1:使用ConcurrentDictionary(推荐)
ConcurrentDictionary是.NET内置的线程安全字典,其GetOrAdd方法是原子操作,无需手动加锁即可保证线程安全,代码更简洁高效:
public class SingletonFactory { private static readonly ConcurrentDictionary<Type, object> _instances = new ConcurrentDictionary<Type, object>(); public static T GetInstance<T>() where T : class, new() { return (T)_instances.GetOrAdd(typeof(T), _ => new T()); } }
方案2:双重锁定(Double-Checked Locking)
如果不想依赖ConcurrentDictionary,可以手动实现双重锁定逻辑,减少锁的竞争开销:
public class SingletonFactory { private static readonly Dictionary<Type, object> _instances = new Dictionary<Type, object>(); private static readonly object _lockObj = new object(); public static T GetInstance<T>() where T : class, new() { // 第一次检查,无锁 if (!_instances.ContainsKey(typeof(T))) { lock (_lockObj) { // 第二次检查,防止多个线程等待锁后重复创建 if (!_instances.ContainsKey(typeof(T))) { _instances[typeof(T)] = new T(); } } } return (T)_instances[typeof(T)]; } }
2. 支持实例销毁的实现方案
原始实现中字典持有实例的强引用,即使实例不再被业务代码使用,也不会被GC回收。要支持自动销毁或手动销毁,可改用**弱引用(WeakReference)**存储实例:
自动销毁(GC回收)
使用WeakReference<object>存储实例,当业务代码不再持有实例的强引用时,GC会自动回收它,下次调用GetInstance<T>()时会重新创建:
public class SingletonFactory { private static readonly ConcurrentDictionary<Type, WeakReference<object>> _weakInstances = new ConcurrentDictionary<Type, WeakReference<object>>(); public static T GetInstance<T>() where T : class, new() { var type = typeof(T); // 检查弱引用是否存活 if (_weakInstances.TryGetValue(type, out var weakRef) && weakRef.TryGetTarget(out var instance)) { return (T)instance; } // 实例已回收或不存在,创建新实例 var newInstance = new T(); _weakInstances.AddOrUpdate(type, _ => new WeakReference<object>(newInstance), (_, oldRef) => { // 并发场景下二次检查,避免重复创建 if (oldRef.TryGetTarget(out var existing)) { return oldRef; } return new WeakReference<object>(newInstance); }); return newInstance; } }
手动销毁
添加手动移除实例的方法,主动清理工厂中的实例引用:
public static void RemoveInstance<T>() { _weakInstances.TryRemove(typeof(T), out _); }
3. 适配带参数的构造函数
原始实现的new()约束要求类必须有无参构造函数,要支持带参数的构造,可通过以下两种方式解决:
方案1:传入工厂委托(推荐)
通过Func<T>委托传入实例创建逻辑,灵活支持任意构造参数,且性能优于反射:
public class SingletonFactory { private static readonly ConcurrentDictionary<Type, object> _instances = new ConcurrentDictionary<Type, object>(); public static T GetInstance<T>(Func<T> factory) where T : class { return (T)_instances.GetOrAdd(typeof(T), _ => factory()); } }
使用示例:
// 带参数构造的类 public class MyService { public MyService(string config, int timeout) { // 初始化逻辑 } } // 获取单例实例 var service = SingletonFactory.GetInstance<MyService>(() => new MyService("dev.config", 3000));
方案2:反射创建实例
通过反射动态匹配构造函数并创建实例,适合无法提前传入工厂委托的场景,但性能略低:
public class SingletonFactory { private static readonly ConcurrentDictionary<Type, object> _instances = new ConcurrentDictionary<Type, object>(); public static T GetInstance<T>(params object[] constructorParams) where T : class { var type = typeof(T); if (_instances.TryGetValue(type, out var instance)) { return (T)instance; } // 匹配对应参数类型的构造函数 var paramTypes = constructorParams.Select(p => p.GetType()).ToArray(); var constructor = type.GetConstructor(paramTypes); if (constructor == null) { throw new InvalidOperationException($"未找到{type.Name}匹配参数类型的构造函数"); } var newInstance = (T)constructor.Invoke(constructorParams); _instances.TryAdd(type, newInstance); return newInstance; } }
使用示例:
var service = SingletonFactory.GetInstance<MyService>("dev.config", 3000);
内容的提问来源于stack exchange,提问作者BotMaster3000
相关产品推荐
相关产品推荐

