You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.17 09:14:56