Я хотел бы знать, в чем разница между инициализацией статического члена в строке, как в:
class Foo
{
private static Bar bar_ = new Bar();
}
или инициализируя его внутри статического конструктора, как в:
class Foo
{
static Foo()
{
bar_ = new Bar();
}
private static Bar bar_;
}





В данном случае не думаю, что здесь есть какая-то практическая разница. Если вам нужна какая-то логика при инициализации статических переменных - например, если вы хотите использовать разные конкретные типы интерфейса при разных условиях, - вы должны использовать статический конструктор. Иначе в моей книге подойдет встроенная инициализация.
class Foo
{
private static IBar _bar;
static Foo()
{
if (something)
{
_bar = new BarA();
}
else
{
_bar = new BarB();
}
}
}
На самом деле, это может иметь большое практическое значение - см. Здесь (разница была «работает» против «не работает»): stackoverflow.com/questions/217932/…
Я считаю описанную там ситуацию очень странной: | Подумав об этом, в своих синглтонах я всегда использую два статических объекта, реальный экземпляр и объект, который нужно заблокировать. Экземпляр, который я всегда создаю в методе свойств, объект блокировки, который я создаю встроенным. Я никогда не думал об этом раньше.
Тобьорн: Похоже, вы обычно выполняете блокировку, когда в этом нет необходимости :) (В большинстве случаев я считаю, что гарантии статической инициализации вполне подходят для синглтонов.)
Наверное, это правильно. Но я считаю, что всегда лучше реализовать блокировку, чем однажды забыть о ней, когда она мне действительно нужна: |
Если у вас есть статический конструктор в вашем типе, он изменяет инициализацию типа из-за того, что флаг до поля больше не применяется.
Это также влияет на порядок инициализации - все инициализаторы переменных выполняются перед статическим конструктором.
Насколько я знаю, это все.
Конкретный пример того, почему это важно: stackoverflow.com/questions/217932/…
По вашей ссылке: The runtime could decide to run the type initializer on loading the assembly to start with. Я предполагаю, что с универсальными классами (class Sample<T>) правила меняются? С разрешением во время компиляции (например, C++) правила будут такими же, но .NET выполняет разрешение во время выполнения. Среда выполнения не будет создавать экземпляры для всех возможных комбинаций, и я сомневаюсь, что при загрузке сборки она будет смотреть вперед, чтобы увидеть, какие типы используются.
@NelsonRothermel: он инициализирует тип при первом использовании созданного типа - поэтому, если вы используете Foo<int>, он инициализирует его тогда, а затем снова запустит инициализатор типа, если вы используете Foo<string>.
И есть предупреждение анализа кода, CA1810, которое рекомендует встроенную инициализацию для повышения производительности. «Статические проверки конструктора могут снизить производительность», - говорится в нем.
Ответ сумеречной зоны: существует разница в порядок исполнения между встроенными инициализаторами и назначением ctor ... когда вы смешиваете экземпляры и статические члены и наследование для загрузки.
For static members, static initializers
Static ctors (execute bottom up)
Base static initializer
Base static ctor and so on
For instance members, initializers in current class execute first
Then initializers in base class execute ( up the chain)
Then top-most base ctor is executed (and we walk down now. Instance ctors execute top-down)
Finally current type's ctor is executed.
Пример :)
public class CBase
{
static Talkative m_Baseob1 = new Talkative("Base Static Initializer-");
static Talkative m_Baseob2;
Talkative m_Baseob3 = new Talkative("Base Inst Initializer");
Talkative m_Baseob4;
static CBase()
{
Console.WriteLine("***MethodBegin: Static Base Ctor");
m_Baseob2 = new Talkative("Base Static Ctor");
Console.WriteLine("***MethodEnd: Static Base Ctor");
}
public CBase()
{
Console.WriteLine("***MethodBegin: Instance Base Ctor");
m_Baseob4 = new Talkative("Base Instance Ctor");
Console.WriteLine("***MethodEnd: Instance Base Ctor");
}
}
public class CDerived : CBase
{
static Talkative m_ob1 = new Talkative("Derived Static Initializer");
static Talkative m_ob2;
Talkative m_ob3 = new Talkative("Derived Inst Initializer");
Talkative m_ob4;
static CDerived()
{
Console.WriteLine("***MethodBegin: Derived Static Ctor");
m_ob2 = new Talkative("Derived Static Ctor");
Console.WriteLine("***MethodEnd: Derived Static Ctor");
}
public CDerived()
{
Console.WriteLine("***MethodBegin: Derived Instance Ctor");
m_ob4 = new Talkative("Derived Instance Ctor");
Console.WriteLine("***MethodEnd: Derived Instance Ctor");
}
}
internal class Talkative
{
public Talkative(string sID)
{
Console.WriteLine(sID + " - Talkative created" );
}
}
# Main function somewhere
CDerived s = new CDerived();
Выход:
Derived Static Initializer - Talkative created
***MethodBegin: Derived Static Ctor
Derived Static Ctor - Talkative created
***MethodEnd: Derived Static Ctor
Derived Inst Initializer - Talkative created
Base Static Initializer- - Talkative created
***MethodBegin: Static Base Ctor
Base Static Ctor - Talkative created
***MethodEnd: Static Base Ctor
Base Inst Initializer - Talkative created
***MethodBegin: Instance Base Ctor
Base Instance Ctor - Talkative created
***MethodEnd: Instance Base Ctor
***MethodBegin: Derived Instance Ctor
Derived Instance Ctor - Talkative created
***MethodEnd: Derived Instance Ctor
Здесь статический конструктор CBase вызывается только потому, что создается экземпляр CDerived. Если вы просто вызываете метод в CDerived, которому не нужно касаться CBase, статический конструктор CBase вообще не будет вызван.
Я был бы удивлен, если бы это было так ... Я упомянул наследование в первом предложении. Моя точка зрения: разница в том, как инициализируется переменная, в зависимости от того, как (инициализатор поля или в ctor), будь то static / inst и находится ли она в иерархии типов. но да .. Мой ответ - излишество для OP q;)
проверить: stackoverflow.com/a/40939743/4038978