C # Статическая проблема конструктора конструкторов - необходимо указать параметр - PullRequest
3 голосов
/ 22 апреля 2010

Возникает повторяющаяся проблема проектирования с некоторыми классами, которые требуют одноразовой инициализации с помощью параметра, такого как имя внешнего ресурса, такого как файл конфигурации.

Например, у меня есть проект corelib, который обеспечивает ведение журнала, настройку и общие вспомогательные методы для всего приложения. Этот объект может использовать статический конструктор для инициализации, но ему нужен доступ к файлу конфигурации, который он не может найти сам.

Я вижу пару решений, но оба они кажутся не совсем правильными:

1) Использовать конструктор с параметром. Но тогда каждый объект, который требует функциональности corelib, должен также знать имя файла конфигурации, так что это должно быть передано приложению. Кроме того, если бы я реализовал corelib как синглтон, мне также пришлось бы передать файл конфигурации в качестве параметра методу GetInstance, что, я считаю, также неправильно.

2) Создайте статическое свойство или метод для передачи через файл конфигурации или другой внешний параметр.

Я как бы использовал последний метод и создал метод Load, который инициализирует внутренний класс, который он передает через файл конфигурации в конструкторе. Затем этот внутренний класс предоставляется через открытое свойство MyCoreLib.

public static class CoreLib
{
    private static MyCoreLib myCoreLib;

    public static void Load(string configFile)
    {
        myCoreLib = new MyCoreLib(configFile);
    }

    public static MyCoreLib MyCoreLib
    {
        get { return myCoreLib; }
    }

    public class MyCoreLib
    {
        private string configFile;

        public MyCoreLib(string configFile)
        {
            this.configFile = configFile;
        }

        public void DoSomething()
        {
        }
    }
}

Я все еще не счастлив, хотя. Внутренний класс не инициализируется до тех пор, пока вы не вызовете метод load, поэтому его необходимо учитывать везде, где осуществляется доступ к MyCoreLib. Также ничто не мешает кому-то снова вызывать метод загрузки.

Какие-нибудь другие шаблоны или идеи, как этого достичь?

Ответы [ 6 ]

1 голос
/ 22 апреля 2010

Вам нужно общее местоположение, чтобы сохранить это. Вы можете использовать app.config, даже если это отдельная сборка, определив раздел конфигурации в сборке библиотеки и ссылаясь на него, вы продолжите app.config. Или вы можете просто добавить общие настройки в appSettings и ссылаться на них без использования строго типизированных настроек. Если значение введено пользователем, вы можете использовать изолированное хранилище. Наконец, вы можете поместить его в хорошо известное место в реестре во время установки.

Для кода лучше инкапсулировано следующее

    public interface ICoreLib
    {
        void SomeMethod();
    }
    public static class CoreLibManager
    {
        private static ICoreLib coreLib;
        private static volatile bool initialized;
        private static readonly object lockObject = new object();
        public static ICoreLib CoreLib
        {
            get
            {
                Inititialize();
                return coreLib;
            }
        }

        /// <summary>
        /// The inititialize.
        /// </summary>
        private static void Inititialize()
        {
            if (initialized)
            {
                lock (lockObject)
                {
                    if (!initialized)
                    {
                        string configFile =  // Fech from common location
                        coreLib = new MyCoreLib(configFile);
                        initialized = true;
                    }
                }
            }
        }

        /// <summary>
        /// The my core lib.
        /// </summary>
        private class MyCoreLib : ICoreLib
        {
            public MyCoreLib(string configPath)
            {
            }
            public void SomeMethod()
            {
            }
        }
    }
1 голос
/ 22 апреля 2010

Для этого вы можете использовать систему конфигурации .net. Самый простой способ сделать это - использовать элемент <appsettings> в файле web.config или в файле appname.exe.config. Использование:

ConfigurationManager.AppSettings["property_name"]

для доступа к собственности. Независимо от того, работает ли ваш код в веб-контексте или как приложение для Windows, система конфигурации найдет файл конфигурации и загрузит нужные вам значения.

Вы также можете построить более сложную систему с безопасными типами конфигурационными значениями и иерархическими данными, но мои потребности были довольно просты, поэтому я не стал исследовать это после первой головной боли, заданной в:)

1 голос
/ 22 апреля 2010

Когда у вас есть глобальное состояние, подобное этому, которое вам нужно инициализировать, и для завершения инициализации требуется внешний ввод (такой как файл конфигурации), то вы застряли с внешним кодом, который знает о вводе, вызывающем Load или Инициализируйте, чтобы инициализировать ваше глобальное состояние. Обойти это невозможно.

Однако проблема, которую вы правильно заметили, заключается в том, что любой может попытаться использовать глобальное состояние до того, как оно будет должным образом инициализировано, что является недостатком его показа таким образом. Вы можете обойти это путем рефакторинга всех частей вашей глобальной библиотеки с сохранением состояния в класс экземпляра и передачи ссылок на этот экземпляр через ваше приложение. Поскольку вы контролируете, когда он создается и инициализируется, теперь вы можете убедиться, что он имеет действительное состояние, прежде чем передать его. Вы теряете удобство глобального состояния для лучшей изоляции, которую вы ищете.

0 голосов
/ 23 апреля 2010

ОК, спасибо всем за вашу помощь. Я реорганизовал проект CoreLib и разбил обработку конфигурации на отдельный проект. Теперь у нас есть общий класс для управления конфигурацией. Класс может позаботиться о себе с помощью пользовательской настройки, которая предоставляется через статическое свойство ConfigFile. Это свойство также сохраняет измененное местоположение файла, если пользователь изменяется через какое-то диалоговое окно конфигурации. Также инициализированный флаг будет сброшен при изменении файла конфигурации.

public interface IConfig
{
    void SomeMethod();
}

public static class ConfigurationManager
{
    private static IConfig config;
    private static volatile bool initialized;
    private static readonly object lockObject = new object();

    public static string ConfigFile
    {
        get { return Properties.Settings.Default.ConfigFile; }
        set
        {
            if (Properties.Settings.Default.ConfigFile == value) return;

            lock (lockObject)
            {
                Properties.Settings.Default.Save();
                initialized = false;
            }
        }
    }

    public static IConfig Config
    {
        get
        {
            Inititialize();
            return config;
        }
    }

    private static void Inititialize()
    {
        lock (lockObject)
        {
            if (initialized) return;

            config = new Configuration(Properties.Settings.Default.ConfigFile);
            initialized = true;
        }
    }
}

internal class Configuration : IConfig
{
    public ClientConfig(string configFile)
    {
        // Parse & validate config file
    }

    public void SomeMethod()
    {
    }
}

Итак, теперь при запуске мы сначала проверяем постоянный параметр ConfigFile, а затем пытаемся получить доступ к экземпляру конфигурации через свойство Config менеджера. Любые исключения при разборе могут обрабатываться здесь и обрабатываться соответствующим образом. Затем разработчик должен обработать любые исключения для методов IConfig.

if (!System.IO.File.Exists(ConfigurationManager.ConfigFile))
{
    // Display config file locator dialog
    ConfigurationManager.ConfigFile = someDialog.FileName;
}

try
{
    IConfig config = ConfigurationManager.Config;
}
catch
{
    // Error parsing config file
}
0 голосов
/ 22 апреля 2010

Альтернатив не будет много.

Либо вы передаете материал (в этом случае я бы не передавал строку, а создавал конкретный (нестатический) CoreLib и передавал ее, либо вы делаете то, что предлагали.

Не забудьте скрыть конструктор для MyCoreLib. В настоящее время это общедоступное, что, вероятно, непреднамеренно.

0 голосов
/ 22 апреля 2010

Вы можете сделать класс одноэлементным, что обеспечит наличие только одного его экземпляра, а также позволит использовать параметр конструктора.

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