Tidbits

My method for static methods on generic type parameters

Sorry to disappoint the people most likely to stumble upon this, I have no fancy method for providing real static methods in a generic context.
Short version:

  • Find the method through reflection
  • Compile into a Func
  • Store in generic base class

The idea is to call a method from a base class, which redirects the call to the intended method.


Let me give you an example right away, below it you’ll find the explanation:

public abstract class Parent<Twhere T : Parent<T>
{
    //Since this is a generic class, we'll have one Func for each T
    private static readonly Func<stringint> ChildGetCount;
 
    static Parent()
    {
        //Find the method
        Type[] types = { typeof(string) };
        MethodInfo methodInfo = typeof(T).GetMethod
        (
            nameof(GetCount), 
            BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.DeclaredOnly, 
            null, 
            types, 
            Array.Empty<ParameterModifier>()
        );
 
        //Verify the method exists and throw a dedicated exception if it doesn't (to make debugging easier)
        if (methodInfo is null)
            throw new MissingMethodException(typeof(T).FullName, nameof(GetCount));
 
        //Build an Expression to speed up performance
        ParameterExpression parameterExpression = Expression.Parameter(types[0]);
        MethodCallExpression methodCallExpression = Expression.Call(methodInfo, parameterExpression);
        Expression<Func<stringint>> expression = Expression.Lambda<Func<stringint>>
        (
            methodCallExpression, 
            parameterExpression
        );
 
        //Store the Func for later use
        ChildGetCount = expression.Compile();
    }
 
    public static int GetCount(string value) => ChildGetCount(value);
}
 
public class ChildA : Parent<ChildA>
{
    private new static int GetCount(string value) => value.Count(c => c == 'a');
}
 
public class ChildB : Parent<ChildB>
{
    private new static int GetCount(string value) => value.Count(c => c == 'b');
}
 
public class ChildC : Parent<ChildC>
{
    private new static int GetCount(string value) => value.Count(c => c == 'c');
}

One of the most important parts of this whole thing is the generic base class that is constrained to itself.
This way, due to the way generics work, we can force a unique parent class for each child type. And each of those parents will have the information about what exact type the child is.
By using reflection, we can now search for a specific method in that child type, and store a reference to it in the childs unique parent.
To improve performance I use System.Linq.Expression to compile the MethodInfo into a Func, but that’s not necessary to make all of this work.
Finally, we add a static method in the parent, which simply redirects the call to the stored Func.
Another unnecessary detail: I used the same name for the method in the parent and child classes.
This was mostly for cohesion, but needing the new keyword is also a good reminder that you have or haven’t “overridden” the required method yet.


With the code above we can now do this:

const string value = "a ab abc";
 
int countA = ChildA.GetCount(value);//3
int countB = ChildB.GetCount(value);//2
int countC = ChildC.GetCount(value);//1

Which we could have done with plain static methods too, of course. But we can also do this:

const string value = "a ab abc";
 
int genericCountA = Parent<ChildA>.GetCount(value);//3
int genericCountB = Parent<ChildB>.GetCount(value);//2
int genericCountC = Parent<ChildC>.GetCount(value);//1

Which finally allows for things like these, which were the whole reason I needed all of this in the first place:

public static int GenericGetCount<T>(string valuewhere T : Parent<T>
    => Parent<T>.GetCount(value);
const string value = "a ab abc";
 
int genericCountA = GenericGetCount<ChildA>(value);//3
int genericCountB = GenericGetCount<ChildB>(value);//2
int genericCountC = GenericGetCount<ChildC>(value);//1

Downsides to this approach:

First of all, this needs a very specific generic structure. Which can not always be done, mostly because you might already use generics for other reasons. At least not without some additional complications.

Secondly, the compiler doesn’t verify anything. You can add additional checks in the static initializer, and a simple test framework can verify if all children are working correctly. But it’s still noway near the level of compiler enforced security that you get with proper inheritance. It also requires more awareness on the side of the developer, making it less comfortable to work with.

Lastly, the performance impact isn’t something you should blindly ignore. Reflection is still relatively costly. And the generic base class means we have a lot of additional classes once compiled (one for each child). Nothing too disruptive, but certainly something to keep in mind.

Tidbits

Things I didn’t know until checking the IL

I have grown the habit of doing a couple of things when writing code that I initially thought would improve performance, even if only by a teensy tiny bit.
Well, turns out most of that was pretty much useless. Not hurting anything, but not helping either.
How did I learn that? By having a look at the IL. And here’s a collection of things I found out, that I didn’t know about before.

The code was compiled in Visual Studio for the .NET Framework 4.8 with MSBuild 16.2.0 and the Optimize flag set to true.
The IL was read with the IL Viewer of JetBrains ReSharper.


i++ or ++i

My assumption always was, that the later is slightly faster, since the old value doesn’t need to be “remembered” for use in whatever context it’s used in. Turns out, not really.
These two lines result in the exact same IL:

++foo;
foo++;

If there is a bit more going on, the code actually has a difference:

foo = ++foo;
    IL_0002: ldloc.0      // foo
    IL_0003: ldc.i4.1
    IL_0004: add
    IL_0005: dup
    IL_0006: stloc.0      // foo
    IL_0007: stloc.0      // foo
foo = foo++;
    IL_000c: ldloc.0      // foo
    IL_000d: dup
    IL_000e: ldc.i4.1
    IL_000f: add
    IL_0010: stloc.0      // foo
    IL_0011: stloc.0      // foo

The “remembering” part is simply the duplication moved further up, before the increment.
Same operations, different order.
The only real difference this makes, is that the first option has at most 2 elements on the stack, the second 3. Slight win for my preference? But generally not important. Especially since what ever else you’re doing is probably going to need a bigger stack anyway.


Variable declarations inside or outside of loops

Not sure where I got this from, but basically I used to always declare variables outside of a loop, even if I only need it inside. I think the idea in my head was, that declaring it would require resources at that point in the program, and declaring it inside a loop would cost me those resources every iteration.
Nope.

while (true)
{
    string line = Console.ReadLine();
    Console.WriteLine($"Input was: {line}");
 
    if (line == "exit")
        break;
}

string line;
while (true)
{
    line = Console.ReadLine();
    Console.WriteLine($"Input was: {line}");
 
    if (line == "exit")
        break;
}

Both result in the same IL … until you add a second loop.

string line;
while (true)
{
    line = Console.ReadLine();
    Console.WriteLine($"Input was: {line}");
 
    if (line == "exit")
        break;
}
while (true)
{
    line = Console.ReadLine();
    Console.WriteLine($"Input was: {line}");
 
    if (line == "exit")
        break;
}

while (true)
{
    string line = Console.ReadLine();
    Console.WriteLine($"Input was: {line}");
 
    if (line == "exit")
        break;
}
while (true)
{
    string line = Console.ReadLine();
    Console.WriteLine($"Input was: {line}");
 
    if (line == "exit")
        break;
}

Declaring line outside the loop means the exact same variable is used both times, or better, the exact same memory address. In the second example the compiler ignores that option and uses two different variables, one for each loop.
I suspect that at runtime the JIT compiler, which does a lot more optimization, sees the obvious potential for reuse and makes the IL difference meaningless.
I do tend to declare variables inside the loop nowadays, unless I explicitly want them kept “alive” beyond their scope.


for or foreach

Let’s assume you got an array and want to iterate through it. Do you use a for or foreach loop?
I used to think foreach adds overhead, because it treats the array as an IEnumerable and creates an Enumerator.
It doesn’t.
These two loops

for (int i = 0; i < bar.Length; ++i)
    sum += bar[i];
foreach (int i in bar)
    sum += i;

do look different in the IL, but they do the same basic thing.
The foreach loop uses a variable it increments each iteration to use as index for the array.
Once that index reaches the arrays length, the loop ends. Pretty much like the for loop. This only applies to arrays though, List for example has an indexer, but foreach will use the enumerator!


! is null or is object

In the new C# versions there is a neat language addition, but if var is not null is not an option for you, there is either

if (!(something is null))

or

if (something is object)

Both have the same effect. But does the later do more stuff? No. Same IL.

    IL_0006: ldloc.0      // something
    IL_0007: brfalse.s    IL_000e

Same with these two.

var = !(something is null);
var = something is object;
    IL_0006: ldloc.0      // something
    IL_0007: ldnull
    IL_0008: cgt.un
    IL_000a: stloc.1      // var

switch or else if

If you have several different branches depending on a single value, you often use a switch. But what if there are only two options? Would an if/else if have a better performance? Short answer: Maybe, but don’t bother. Because the compiler makes that choice for you already.
Actually, switch is quite often compiled into something closer to if/else if, you just get special switch IL when you have neatly arranged integer values. And even then there are some interesting constellations, like splitting your switch into several individual ones.
So when a switch makes your code more readable, or simply easier to expand in the future, just use a switch.


Repeat typeof or store Type

nameof is evaluated during compilation, and all that remains in the compiled code is a string constant. That means I can use it as often as I want, without worrying about performance.
How about typeof though? What does it do in the IL? If I reference the type several times, can I still reuse typeof or is it better to cache the value?

string typeFullName = typeof(A).FullName;
bool typeIsEnum = typeof(A).IsEnum;
bool typeIsSerializable = typeof(A).IsSerializable;
    // [14 13 - 14 54]
    IL_0000: ldtoken      IlCode.A
    IL_0005: call         class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle)
    IL_000a: callvirt     instance string [mscorlib]System.Type::get_FullName()

    // [15 13 - 15 48]
    IL_000f: ldtoken      IlCode.A
    IL_0014: call         class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle)
    IL_0019: callvirt     instance bool [mscorlib]System.Type::get_IsEnum()
    IL_001e: stloc.0      // typeIsEnum

    // [16 13 - 16 64]
    IL_001f: ldtoken      IlCode.A
    IL_0024: call         class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle)
    IL_0029: callvirt     instance bool [mscorlib]System.Type::get_IsSerializable()
    IL_002e: stloc.1      // typeIsSerializable

As you can see, each typeof results in a method call to Type.GetTypeFromHandle. Storing the result seems the better option to me.

Type type = typeof(A);
string typeFullName = type.FullName;
bool typeIsEnum = type.IsEnum;
bool typeIsSerializable = type.IsSerializable;
    // [14 13 - 14 35]
    IL_0000: ldtoken      IlCode.A
    IL_0005: call         class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle)

    // [15 13 - 15 49]
    IL_000a: dup
    IL_000b: callvirt     instance string [mscorlib]System.Type::get_FullName()
    IL_0010: stloc.0      // typeFullName

    // [16 13 - 16 43]
    IL_0011: dup
    IL_0012: callvirt     instance bool [mscorlib]System.Type::get_IsEnum()
    IL_0017: stloc.1      // typeIsEnum

    // [17 13 - 17 59]
    IL_0018: callvirt     instance bool [mscorlib]System.Type::get_IsSerializable()
    IL_001d: stloc.2      // typeIsSerializable

Good thing I did it this way already!