Инициализация библиотеки при загрузке сборки - PullRequest
25 голосов
/ 20 января 2009

У меня есть библиотека .net, которая действует как функциональная библиотека. Существует множество статических типов наряду со статическими методами.

Существует некоторый код инициализации, который мне нужно запустить, чтобы настроить библиотеку, готовую к использованию.

Когда сборка загружается, есть ли способ обеспечить запуск определенного метода? Что-то вроде AppDomain.AssemblyLoad, но вызывается автоматически из самой сборки. Я думал, что, может быть, есть что-то вроде AssemblyAttribute, который можно использовать?

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

Спасибо!

Ответы [ 4 ]

19 голосов
/ 16 марта 2012

Да, есть - вроде.

Воспользуйтесь отличной маленькой утилитой Эйнара Эгильссона, InjectModuleInitializer .

Запустите этот исполняемый файл в качестве шага после сборки, чтобы создать небольшую функцию .cctor (функцию инициализатора модуля), которая вызывает вашу статическую пустую функцию, которая не принимает параметров. Было бы хорошо, если бы компилятор дал нам возможность создавать .cctor (), к счастью, нам редко нужна эта возможность.

Однако это не полная замена DllMain. CLR вызывает эту функцию .cctor только перед любыми методами, вызываемыми в вашей сборке, а не при загрузке сборки. Итак, если вам нужно, чтобы что-то происходило при загрузке сборки, вам нужно, чтобы код загрузки вызывал метод напрямую или использовал хак, который я подробно описал https://stackoverflow.com/a/9745422/240845

11 голосов
/ 24 января 2011

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

У меня была похожая проблема, и я решил ее, создав атрибут для инициализации 'InitializeOnLoad' с параметром Type. Затем в основной исполняемый файл я добавил тривиальный обработчик AppDomain.AssemblyLoaded, который сканирует вновь загруженную сборку для указанного выше атрибута и вызывает для них System.Runtime.CompilerServices.RuntimeHelpers.RunClassConstructor ().

[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
public class InitializeOnLoadAttribute : Attribute
{
    Type type;

    public InitializeOnLoadAttribute(Type type) { this.type = type; }

    public Type Type { get { return type; } }
}

// somewhere very early in main exe initialization
AppDomain.CurrentDomain.AssemblyLoad += new AssemblyLoadEventHandler(AssemblyInitializer);

static void AssemblyInitializer(object sender, AssemblyLoadEventArgs args)
{
    // force static constructors in types specified by InitializeOnLoad
    foreach (InitializeOnLoadAttribute attr in args.LoadedAssembly.GetCustomAttributes(typeof(InitializeOnLoadAttribute), false))
        System.Runtime.CompilerServices.RuntimeHelpers.RunClassConstructor(attr.Type.TypeHandle);
}

Кроме того, если вы боитесь, что сборки могли быть загружены до того, как вы перехватили событие AssemblyLoad, вы можете просто запустить AppDomain.GetAssemblies () и вызвать для них «инициализатор».

7 голосов
/ 20 января 2009

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

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

2 голосов
/ 02 апреля 2013

Это возможно - просто добавьте статический конструктор в класс <Module>. Хотя я не знаю, как это сделать без модификации IL.

...