Как: Автоматическое создание синглтона в C # - PullRequest
6 голосов
/ 28 апреля 2009

Я хочу иметь Singleton, который будет автоматически создан при запуске программы.

То, что я подразумеваю под "автоматическим созданием экземпляров", заключается в том, что код в Singleton должен создавать себя при запуске программы без каких-либо вызовов или объявлений другим кодом.

Итак, я хочу, чтобы что-то вроде следующего было создано и записано «MySingleton Instantiated» при запуске программы (без выполнения основного кода) ...

static class MySingleton
{
    private static MySingleton self = new MySingleton();

    protected MySingleton()
    {
        System.Console.WriteLine("MySingleton Instantiated");
    }
}

за исключением того, что это не работает, поскольку C # будет инициализировать статические члены класса только тогда, когда это необходимо, т.е. когда к ним обращаются / и т.д.

Так что мне делать? это можно сделать?

Я не делал этого лично с C ++ (давно не использовал C ++), но я почти уверен, что это можно сделать в C ++, но не уверен насчет C #.

Любая помощь приветствуется. Спасибо.


Что я на самом деле хочу сделать, это ... Было бы много таких одноэлементных классов (и с течением времени их можно было бы добавить), и все они наследовали бы от общего (абстрактного) родительского класса (он же PClass).

PClass будет иметь статический член, который представляет собой коллекцию PClasses ... и конструктор для добавления себя в коллекцию ...

Тогда в теории все синглеты будут автоматически добавлены в коллекцию (так как, когда они создаются, вызывается базовый конструктор PClass и добавляет новый объект в коллекцию) ... тогда коллекция может использоваться, ничего не зная о какие дочерние (одноэлементные) классы были реализованы, и новые дочерние (одноэлементные) классы могут быть добавлены в любое время без необходимости изменения какого-либо другого кода.

К сожалению, я не могу заставить детей (одиночек) создавать свои экземпляры ... испортить мой маленький план, в результате чего получается этот пост.

Надеюсь, я объяснил это достаточно хорошо.


PS. Да, я понимаю, что есть плохие чувства к синглетонам и их использованию ... но иногда они полезны, и даже если бы сам сатана делал синглтоны, я все равно хотел бы знать, можно ли решить мою проблему в C #. Спасибо всем вам.

Ответы [ 8 ]

6 голосов
/ 28 апреля 2009

Упомянутый Крисом подход IoC, вероятно, является лучшим, но в случае неудачи «лучшее» решение, которое я могу придумать, состоит в том, чтобы сделать что-то напуганное с отражением и атрибутами в соответствии с:

public class InitOnLoad : Attribute 
{ 
    public static void Initialise()
    {
        // get a list of types which are marked with the InitOnLoad attribute
        var types = 
            from t in AppDomain.CurrentDomain.GetAssemblies().SelectMany(a => a.GetTypes())
            where t.GetCustomAttributes(typeof(InitOnLoad), false).Count() > 0
            select t;

        // process each type to force initialise it
        foreach (var type in types)
        {
            // try to find a static field which is of the same type as the declaring class
            var field = type.GetFields(System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic).Where(f => f.FieldType == type).FirstOrDefault();
            // evaluate the static field if found
            if (field != null) field.GetValue(null);
        }
    }
}

[InitOnLoad]
public class Foo
{
    public static Foo x = new Foo();

    private Foo()
    {
        Console.WriteLine("Foo is automatically initialised");
    }
}

public class Bar
{
    public static Bar x = new Bar();

    private Bar()
    {
        Console.WriteLine("Bar is only initialised as required");
    }
}

С вызовом InitOnLoad.Initialise (), добавленным в ваш основной метод.

Вы можете покончить с атрибутом, но это может привести к инициализации ненужных типов и ненужному потреблению памяти (например, Bar в приведенном выше коде).

Стоит также отметить, что он не будет обрабатывать типы, содержащиеся в каких-либо сборках, которые загружаются динамически, если только вы не сделаете еще один вызов Initialise, но на основе вашего вопроса («автоматическое создание экземпляра при запуске программы»), который не звучит как вопрос для вас.

5 голосов
/ 28 апреля 2009

Хотя модули .NET теоретически (IIRC) могут реагировать на загрузку модулей и т. Д., Это не доступно через C #. В некоторых средах (например, ASP.NET) есть ловушки, которые вы можете использовать через конфигурацию, например, взломать их через обработчик или через global.asax.cs - однако для обычного приложения на C # (console, winform и т. Д.) Вам придется вызвать его вручную. Например, статический конструктор класса, в котором находится точка входа Main, будет вызван.

Итак: какой здесь вариант использования? Когда подход ленивой загрузки не будет в порядке?

4 голосов
/ 28 апреля 2009

Исходя из того, что вы пытаетесь сделать, я бы отказался от идеи настоящего синглтона и вместо этого использовал бы библиотеку IoC.

Проверьте StructureMap, Castle Windsor, Ninject и / или Autofac.

Это позволит вам создать класс как синглтон, с помощью библиотеки IoC, иметь столько, сколько вы хотите, но это просто старый класс.

Проблема синглетонов заключается в том, что они действительно портят тестируемость (через модульное тестирование) вашего приложения.

Просто выполните поиск в Google по "Синглтон считается вредным", и вы увидите еще много ссылок.

Кроме того, вы также можете использовать простой шаблон фабрики классов / методов.

2 голосов
/ 28 апреля 2009

Я сомневаюсь, что это возможно, ничего не делая из Main. Даже добавление static MySingleton() {} в класс не гарантирует его создания, если вы его не используете.

1 голос
/ 28 апреля 2009

Тангенциально, позвольте мне указать, что на самом деле это не одноэлементный шаблон, как это обычно делается. Вот нормальная (упрощенная) реализация в C # (не включая такие вещи, как безопасность потоков для краткости):

public sealed class Singleton
{
static readonly Singleton _instance = new Singleton();

// private ctor
Singleton() {}

 public static Singleton Instance
 {
  get { return _instance; }
 }
}

Эта строка из вашего кода даже не должна компилироваться!

protected MySingleton()

Сказав все это, я согласен с парнем, который спросил о вашем сценарии использования. Это было бы полезно знать. =)

1 голос
/ 28 апреля 2009

Вы в основном просите .NET Framework вызывать некоторую функцию всякий раз, когда она загружает сборку. Эта функция будет регистратором экземпляров PClass.

В C # нет DllMain. Вы можете смоделировать это, имея атрибут уровня сборки и некоторый код в вашем методе main, который прослушивает каждый раз, когда загружается сборка. Атрибут может иметь указанную точку входа "DllMain" или указывать на ваши унаследованные классы PCIass.

0 голосов
/ 28 апреля 2009

"PClass будет иметь статический член, который является коллекцией PClasses ..."

Звучит как то, что можно легко сделать с помощью Reflection. Вам не нужен код, который запускается при запуске; вы можете просто создать список этих классов, когда вам это нужно.

Вот пример, который загружает список один раз, когда вы впервые читаете свойство Instances, просматривает все сборки, загруженные в то время (вы могли бы немного упростить это, если бы вы хотели смотреть только в той же сборке, что и PClass , но ваш вопрос не уточнил, в какие сборки вы хотите посмотреть), и добавит в список один экземпляр любого потомка PClass (но не самого PClass). Каждому потомку PClass понадобится конструктор без параметров, но вам не понадобятся конструкторы классов.

public class PClass
{
    private static List<PClass> m_instances;
    public static IList<PClass> Instances
    {
        get
        {
            if (m_instances == null)
                m_instances = LoadInstanceList();
            return m_instances;
        }
    }
    private static List<PClass> LoadInstanceList()
    {
        foreach (var assembly in AppDomain.GetAssemblies())
        {
            foreach (var type in assembly.GetTypes())
            {
                if (type.IsAssignableTo(typeof(PClass)) && type != typeof(PClass))
                    m_instances.Add(Activator.CreateInstance(type));
            }
        }
    }
}
0 голосов
/ 28 апреля 2009

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

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