使用反射和锁定正确实例化分配给私有静态volatile变量的类



所以这是一个我想要改进或确认的人为例子。

我正在使用(my/I)BATIS。. NET(一个轻量级的ORM/数据映射器框架),我得到的是一个类,其中包含对数据库的每个表映射器的静态引用。它工作得很好,但是有太多的重复,我认为可能有机会大大简化代码。类当前看起来像这样:

public sealed class MyRepository
{
    private static string _connectionString;
    private volatile static TableAbcMapper _tableAbcMapper;
    private volatile static TableXyzMapper _tableXyzMapper;
    // and about 30 more of these
    private MyRepository()
    {
    }
    public static void Init(string connectionString)
    {
        _connectionString = connectionString;
    }
    public static string ConnectionString
    {
        get { return _connectionString; }
    }
    public static TableAbcMapper TableAbc
    {
        get
        {
            if (_tableAbcMapper == null)
            {
                lock (typeof(TableAbcMapper))
                {
                    if (_tableAbcMapper == null)
                    {
                        _tableAbcMapper = new TableAbcMapper(_connectionString);
                    }
                }
            }
            return _tableAbcMapper;
        }
    }
    public static TableXyzMapper TableXyz
    {
        get
        {
            if (_tableXyzMapper == null)
            {
                lock (typeof(TableXyzMapper))
                {
                    if (_tableXyzMapper == null)
                    {
                        _tableXyzMapper = new TableXyzMapper(_connectionString);
                    }
                }
            }
            return _tableXyzMapper;
        }
    }
    // and about 30 more of these readonly properties
}

每次我在数据库中添加或删除一个表时,我都要添加一个private volatile static字段和那个大而丑陋的单例属性到MyRepository类。我的第一个想法是使属性调用类中的泛型实例化函数;看起来像:

private static void InitMapper<TMapper>(TMapper instance) where TMapper : MyMapper
{
    lock (typeof(TMapper))
    {
        if (instance == null)
        {
            instance = Activator.CreateInstance(typeof(TMapper), 
                new object[] { _connectionString }) as TMapper;
        }
    }
}

那么公共getter可以稍微简化为:

public static TableXyzMapper TableXyz
{
    get
    {
        if (_tableXyzMapper == null)
        {
            InitMapper<TableXyzMapper>(_tableXyzMapper);
        }
        return _tableXyzMapper;
    }
}

但是,我不知道是否传递volatile字段是一个好主意,使用refout与volatile字段是一个禁忌,最重要的是,它并没有减少所有的代码量。

我想做的是完全重构MyRepository类,这样它就没有私有字段和公共getter,并使用反射来初始化所有的映射器,而不是惰性加载它们。我不需要改变任何使用MyRepository类的代码,因为它看起来完全一样,但在底层它会有点不同:

public sealed class MyRepository
{
    private MyRepository()
    {
    }
    public volatile static TableAbcMapper TableAbc = null;
    public volatile static TableXyzMapper TableXyz = null;
    public static void Init(string connectionString)
    {
        foreach (var fieldInfo in typeof(MyRepository).GetFields(BindingFlags.Static))
        {
            if (fieldInfo.GetValue(new MyRepository()) == null)
            {
                lock (fieldInfo.FieldType)
                {
                    if (fieldInfo.GetValue(new MyRepository()) == null)
                    {
                        fieldInfo.SetValue(new MyRepository(), 
                            fieldInfo.FieldType.GetConstructor(new Type[] { typeof(string) })
                                .Invoke(new object[] { connectionString }));
                    }
                }
            }
        }
    }
}

现在,当新表添加到数据库时,我必须做的唯一维护是为它添加一个新的public volatile static字段,反射将照顾其余的。

我对这种方法有几个问题:

  • 这种方法在功能上是否等同于原始类?
  • 使用反射定义易失性变量有任何危险吗?
  • 它是否与原始类一样可读(假设它都被注释了)?

最后,如果这是一个更适合代码审查网站的问题,我完全赞成将其迁移(mod !)。

它可能不会短得多,但是因为你已经有了一个init方法,你可以在那里创建一个惰性值,它是在第一次访问时创建的。Lazy (. net 4的一部分)的好处是,你可以指定值可以多次创建,但它的值只发布一次(提供更好的性能)。

class Program
    {
        static Lazy<string> _Lazy;
        static string _connectionString;
        public string LazyValue
        {
            get
            {
                return _Lazy.Value;
            }
        }
        public static void Init(string connectionString)
        {
            _connectionString = connectionString;
            _Lazy = new Lazy<string>(() => new string(connectionString.ToArray()), System.Threading.LazyThreadSafetyMode.ExecutionAndPublication);
        }

它不会变得更短。

相关内容

  • 没有找到相关文章