I just did a small test to check the impact of adding a static constructor to one of my classes.
I have a base class that looks like this:
public abstract class Base
{
public abstract Task DoStuffAsync();
}
The problem is, that in one of the implementations that method does nothing, so I can set a pre-made completed task and return it every time.
public sealed class Test1 : Base
{
readonly Task _emptyTask;
public Test1()
{
TaskCompletionSource
(Other option is to return the task on demand, but turns out this method is always called)
Objects of this class are created very very often, usually in loops. Looking at it, it looks like setting _emptyTask as a static field would be beneficial since it would be the same Task for all methods:
public sealed class Test2 : Base
{
static readonly Task _emptyTask;
static Test2()
{
TaskCompletionSource source = new TaskCompletionSource();
source.SetResult(null);
_emptyTask = source.Task;
}
public override Task DoStuffAsync()
{
return _emptyTask;
}
}
Then I remember the "issue" with static constructors and performance, and after research a little (that is how I get here), I decide to do a small benchmark:
Stopwatch sw = new Stopwatch();
List test1list = new List(), test2list = new List();
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.");
And the results are quite clear:
Test 1: 53.07 ms.
Test 2: 5.03 ms.
end
So despite of having a static constructor, the benefit outweighs the issue. So always measure.