Почему этот статический конструктор не вызван?
Я создаю веб-сервис asp.net. У меня есть один класс, статический конструктор которого не вызван, когда я пытаюсь инициализировать объект этого класса. Я не могу понять это поведение. Внутри статического конструктора я читаю значения из файла web.config.
Вот часть кода:
[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
public class Service : System.Web.Services.WebService
{
AppController extractor;
public Service()
{
try
{
extractor = new AppController();
}
catch(Exception ex)
{
// I am not getting exception at this point.
}
}
}
public class AppController
{
static string converterBatchFilePath = null;
static string personalProfileOutputFolderPath = null;
static AppController()
{
// reading some settings from web.config file
try
{
converterBatchFilePath = ConfigurationManager.AppSettings["WordToTextConverterBatFilePath"];
}
catch(Exception ex)
{ // }
}
public AppController()
{
// do some initialization
}
}
Во время отладки веб-службы я заметил, что только вызывающий конструктор получает вызов и управление никогда не переходит к статическому конструктору.
Кто-нибудь знает, почему это происходит?
Я использую VS 2008 Express Edition и С#.
ИЗМЕНИТЬ
На самом деле этот AppController - это консольный проект. Я добавил этот проект в качестве ссылки в проекте веб-службы, а затем использовал его. Если я использую AppController из командной строки, он отлично работает, но не работает из проекта веб-службы.
Ответы
Ответ 1
Я предполагаю, что он вызвал, прежде чем вы ожидали, что он будет вызван. Если вы уже отлаживали свой сайт, но не перерабатывали AppPool, очень вероятно, что статический конструктор уже запущен.
Точно так же все, что обращается к любым статическим членам, также вызывает статический конструктор, если он еще не был вызван.
Ответ 2
Сегодня мой статический инициализатор не вызывался. Оказывается, статические инициализаторы не вызываются до доступа к членам-членам класса.
Поскольку значения const известны во время компиляции, это имеет смысл, но это означает, что документация, которая гласит: "Он вызывается автоматически до того, как.. на которые ссылаются любые статические члены", является технически некорректным, по крайней мере, в сочетании с @JonSkeet утверждение, что "Все объявления констант неявно статичны".
Эта программа демонстрирует проблему:
using System;
static class Program
{
public static void Main()
{
Console.WriteLine("Constant={0}", Problem.Constant);
Console.WriteLine("ReadOnly={0}", Problem.ReadOnly);
Console.WriteLine("Field={0}", Problem.Field);
Console.WriteLine("Property={0}", Problem.Property);
}
private static class Problem
{
public const int Constant = 1;
public static readonly int ReadOnly = 2;
public static int Field = 3;
private static int mProperty = 4;
public static int Property { get { return mProperty; } }
static Problem()
{
Console.WriteLine("Problem: static initializer");
}
}
}
Вывод:
Constant = 1
Проблема: статический инициализатор
ReadOnly = 2
Поле = 3
Свойство = 4
(Протестировано против .NET 4.5.)
Ответ 3
Статический конструктор используется для инициализации любых статических данных или для выполнения определенного действия, которое требуется выполнить только один раз. Он вызывается автоматически до создания первого экземпляра или ссылки на любые статические члены.
Обратите внимание, что
статический конструктор вызывается автоматически для инициализации класса до создания первого экземпляра или ссылки на любые статические члены. и пользователь не может контролировать, когда статический конструктор выполняется в программе.
Взято из Статические конструкторы MSDN (руководство по программированию на С#).
Ответ 4
Я подозреваю, что ваша проблема вызвана тем, что исключение возникает в вашем статическом конструкторе и проглочено кодом, который создает экземпляр.
Потенциально даже исключение в инициализаторе статического поля, которые еще сложнее отлаживать.
Возможно, включение прерывания при исключениях с первым шансом помогает отладить проблему.
Я бы не поставил код, который читает из конфигурационного файла в статический конструктор вообще. Я бы вместо этого инкапсулировал все зависимые от конфигурации вещи, которые у вас есть в классе, и передал экземпляр этого класса в ваш конструктор, возможно, используя контейнер IoC.
Это имеет ряд преимуществ:
- Вы можете использовать разные конфигурации в одном и том же AppDomain одновременно
- Вы можете использовать альтернативные способы загрузки конфигурации
- Вам не нужно делать сложные вещи, которые могут быть неудачными в статическом конструкторе. Как вы видели, статические конструкторы, как правило, проблематичны для отладки, поэтому я бы выполнил только простые инициализации, которые не зависят от внешнего состояния там.
(Я знаю, что это не ответ, но он слишком длинный для комментария)
Ответ 5
Вот пример, который я собрал для получения значений из файла конфигурации в вашем классе экстрактора, как в статических, так и в конструкторах экземпляров. Это работает для меня - сравните его с тем, что вы делаете, и посмотрите, что другое:
public class Service : System.Web.Services.WebService
{
AppController extractor;
[WebMethod]
public string HelloWorld()
{
extractor = new AppController();
return AppController.staticString + " :: " + extractor.instanceString;
}
}
class AppController
{
public static string staticString;
public string instanceString;
static AppController()
{
staticString = System.Configuration.ConfigurationManager.AppSettings["static"];
}
public AppController()
{
instanceString = System.Configuration.ConfigurationManager.AppSettings["instance"];
}
}
Мой web.config:
<appSettings>
<add key="static" value="blah blah"/>
<add key="instance" value="ha ha"/>
</appSettings>
Мой ответ:
<?xml version="1.0" encoding="UTF-8"?>
<string xmlns="http://tempuri.org/">blah blah :: ha ha</string>