Я только что провел небольшой тест, чтобы проверить влияние добавления статического конструктора в один из моих классов.
У меня есть базовый класс, который выглядит так:
public abstract class Base
{
public abstract Task DoStuffAsync();
}
Проблема в том, что в одной из реализаций этот метод ничего не делает, поэтому я могу установить заранее выполненное завершенное задание и возвращать его каждый раз.
public sealed class Test1 : Base
{
readonly Task _emptyTask;
public Test1()
{
TaskCompletionSource<Object> source = new TaskCompletionSource<object>();
source.SetResult(null);
_emptyTask = source.Task;
}
public override Task DoStuffAsync()
{
return _emptyTask;
}
}
(Другой вариант - вернуть задачу по требованию, но оказывается, что этот метод всегда вызывается)
Объекты этого класса создаются очень очень часто, обычно в циклах. Глядя на это, похоже, что установка _emptyTask
в качестве статического поля была бы полезной, поскольку она была бы одинаковой Task
для всех методов:
public sealed class Test2 : Base
{
static readonly Task _emptyTask;
static Test2()
{
TaskCompletionSource<Object> source = new TaskCompletionSource<object>();
source.SetResult(null);
_emptyTask = source.Task;
}
public override Task DoStuffAsync()
{
return _emptyTask;
}
}
Затем я вспоминаю «проблему» со статическими конструкторами и производительностью, и после небольшого исследования (вот как я здесь оказался), я решил сделать небольшой тест:
Stopwatch sw = new Stopwatch();
List<Int64> test1list = new List<Int64>(), test2list = new List<Int64>();
for (int j = 0; j < 100; j++)
{
sw.Start();
for (int i = 0; i < 1000000; i++)
{
Test1 t = new Test1();
if (!t.DoStuffAsync().IsCompleted)
throw new Exception();
}
sw.Stop();
test1list.Add(sw.ElapsedMilliseconds);
sw.Reset();
sw.Start();
for (int i = 0; i < 1000000; i++)
{
Test2 t = new Test2();
if (!t.DoStuffAsync().IsCompleted)
throw new Exception();
}
sw.Stop();
test2list.Add(sw.ElapsedMilliseconds);
sw.Reset();
GC.Collect();
}
Console.WriteLine("Test 1: " + test1list.Average().ToString() + "ms.");
Console.WriteLine("Test 2: " + test2list.Average().ToString() + "ms.");
И результаты вполне понятны:
Test 1: 53.07 ms.
Test 2: 5.03 ms.
end
Таким образом, несмотря на наличие статического конструктора, выгода перевешивает проблему. Поэтому всегда измеряйте.