Правильное создание экземпляра класса, назначенного частной статической изменчивой переменной, с использованием отражения и блокировки - PullRequest
4 голосов
/ 28 октября 2011

Итак, вот надуманный пример того, что я хочу улучшить или подтвердить.

Я использую (my / i) BATIS.NET (облегченный каркас ORM / data mapper) и чтоУ меня есть класс со статической ссылкой на каждый из картографов для базы данных.Это прекрасно работает, но повторений так много, что я подумал, что может быть возможность значительно упростить код.Класс в настоящее время выглядит так:

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 и это большое уродливое свойство singleton-y в класс 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;
        }
    }
}

Тогда число публичных получателей может быть немного уменьшено до:

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

Но я не знаю, является ли идеальной идеей обходить изменчивые поляи использование ref или out с изменяемыми полями - это нет-нет, и, кроме того, это не сильно уменьшает объем кода.

Что я хотел бы сделать, так это полностью изменить класс MyRepository, чтобы он не имел закрытых полей и общедоступных получателей, и использовал отражение для немедленной инициализации всех отображателей вместо их ленивой загрузки.,Мне не пришлось бы менять код, использующий класс 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, а остальное позаботится об отражении.

Несколько вопросов, которые у меня естьс этим подходом:

  • Является ли этот подход функционально эквивалентным исходному классу?
  • Существует ли какая-либо опасность в определении изменчивых переменных с помощью отражения?
  • он так же удобен для чтения, как и исходный класс (при условии, что все это прокомментировано)?

Наконец, если этот вопрос лучше подходит для сайта Code Review , я за то, чтобыон мигрировал (моды!).

1 Ответ

1 голос
/ 28 октября 2011

Это может быть не намного короче, но, поскольку у вас уже есть метод 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);
        }

Этоне станет намного короче, хотя.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...