Search code examples
c#.netclassdecompilingc#-11.0

How does the C# compiler produce this long "ID" in file local types?


Given the following 3 file classes:

file class MyClass
{
}

file class MyClass2
{
}

file class MyClass3
{
    public int MyMember { get; init; }
}

By decompiling this C# code, the file classes now look like:

internal class <Class1>FFEAE62924BA736A3C9E8DE39EA3099D227B65B02C0A6BC7FECCB5FF0D5B9D8B3__MyClass
{
}

internal class <Class1>FFEAE62924BA736A3C9E8DE39EA3099D227B65B02C0A6BC7FECCB5FF0D5B9D8B3__MyClass2
{
}

internal class <Class1>FFEAE62924BA736A3C9E8DE39EA3099D227B65B02C0A6BC7FECCB5FF0D5B9D8B3__MyClass3
{
    public int MyMember { get; init; }
}

I understand that <Class1> is the name of the source file where the file class was defined, and MyClass... is the name of the file class.

What I don't understand is this very long, seemingly ID between the two:

FFEAE62924BA736A3C9E8DE39EA3099D227B65B02C0A6BC7FECCB5FF0D5B9D8B3

From what I understand, it seems to vary based on the source file name, which is surprising when the text between two angle brackets, <Class1>, already seems to play a role.

How does the C# compiler produce this ID? I could not find any information about it on the internet. Seems to be undocumented.


Solution

  • I decided to dig further into Roslyn source code. Using a temporary automated tool to find every C# source file that contains FileLocalType, there were about 12 C# source files.

    Eventually, I arrived into here. The code that generates this ID:

    internal static string MakeFileTypeMetadataNamePrefix(string filePath, ImmutableArray<byte> checksumOpt)
    {
        var pooledBuilder = PooledStringBuilder.GetInstance();
        var sb = pooledBuilder.Builder;
        sb.Append('<');
        AppendFileName(filePath, sb);
        sb.Append('>');
        sb.Append((char)GeneratedNameKind.FileType);
        if (checksumOpt.IsDefault)
        {
            // Note: this is an error condition.
            // This is only included for clarity for users inspecting the value of 'MetadataName'.
            sb.Append("<no checksum>");
        }
        else
        {
            foreach (var b in checksumOpt)
            {
                sb.AppendFormat("{0:X2}", b);
            }
        }
        sb.Append("__");
        return pooledBuilder.ToStringAndFree();
    }
    

    Definition

    After looking further, I can infer that the mysterious ID here is the full (absolute) path to the source file where the file class is defined, hashed using SHA256 hashing algorithm (sometimes SHA1 apparently, but SHA256 by default). And yes, I really do need this algorithm instead of just generating a random integer and then stringifying the result, which would be a traditional way of doing this, but, that answers the question.

    And for some reason these IDs prepend with an 'F', because of this line of code in the same MakeFileTypeMetadataNamePrefix method:

    sb.Append('>');
    /*HERE -->*/sb.Append((char)GeneratedNameKind.FileType);
    if (checksumOpt.IsDefault)
    

    In my case, this long hashed string here:

    FFEAE62924BA736A3C9E8DE39EA3099D227B65B02C0A6BC7FECCB5FF0D5B9D8B3
    

    is the result of this string being hashed using SHA256 algorithm, whose result is prepended with an 'F':

    C:\Users\User\source\repos\TemporaryAppRepository\FileClassTest\Class1.cs