How to get types from assembly in C#

By FoxLearn 2/4/2025 4:46:16 AM   119
In C#, you can extract type information from an assembly through a reflection-only load. This process allows you to examine metadata without executing the assembly, which can help avoid runtime errors that typically occur with a fully loaded assembly.

Reflection-only loading methods differ between the .NET Framework and .NET Core. Below are examples of how to perform reflection-only loading in both environments.

.NET Framework - Use Assembly.ReflectionOnlyLoadFrom()

In a .NET Framework project, you can use the Assembly.ReflectionOnlyLoadFrom() method to load an assembly in reflection-only mode.

var assemblyPath = @"C:\Projects\SampleLibrary\bin\Debug\SampleLibrary.dll";

var assembly = Assembly.ReflectionOnlyLoadFrom(assemblyPath);

foreach (var type in assembly.GetTypes())
{
    Console.WriteLine(type.Name);
}

This code will output the following class name:

SampleClass

.NET Core - Use MetadataReader or MetadataLoadContext

In .NET Core, Assembly.ReflectionOnlyLoadFrom() is not supported and will throw a System.PlatformNotSupportedException. Instead, you can use System.Reflection.Metadata.MetadataReader or System.Reflection.MetadataLoadContext. Below are examples of how to utilize each.

For example, Using MetadataReader

MetadataReader allows you to read the metadata of an assembly without fully loading it.

using System.Reflection.Metadata;
using System.Reflection.PortableExecutable;

var assemblyPath = @"C:\Projects\BackgroundLogger\bin\Debug\net5.0\BackgroundLogger.dll";

using (var sr = new StreamReader(assemblyPath))
{
    using (var portableExecutableReader = new PEReader(sr.BaseStream))
    {
        var metadataReader = portableExecutableReader.GetMetadataReader();

        foreach (var typeDefHandle in metadataReader.TypeDefinitions)
        {
            var typeDef = metadataReader.GetTypeDefinition(typeDefHandle);

            if (string.IsNullOrEmpty(metadataReader.GetString(typeDef.Namespace)))
                continue; // Skip if the namespace is empty, it's likely not a user-defined type

            if (typeDef.Attributes.HasFlag(TypeAttributes.Abstract) || !typeDef.Attributes.HasFlag(TypeAttributes.Public))
                continue; // Skip non-public concrete types

            Console.WriteLine(metadataReader.GetString(typeDef.Name));
        }
    }
}

This will list all public, concrete user-defined types in the assembly:

MainClass
Startup
LoggerService
LoggerConfig
LoggingEnum
LogStruct
LogMessage
LogRepository

This method doesn't differentiate between class, struct, or enum types. If you need to filter for classes only, additional logic is needed.

For example, Using MetadataLoadContext

MetadataLoadContext provides another approach for reflection-only loading in .NET Core. It allows you to load assemblies and work with them using reflection APIs. First, install the System.Reflection.MetadataLoadContext NuGet package with:

Install-Package System.Reflection.MetadataLoadContext

Then, use MetadataLoadContext to extract type information from the assembly:

using System.Reflection;
using System.Runtime.InteropServices;

var assemblyPath = @"C:\Projects\BackgroundLogger\bin\Debug\net5.0\BackgroundLogger.dll";

var resolver = new PathAssemblyResolver(new List<string>(Directory.GetFiles(RuntimeEnvironment.GetRuntimeDirectory(), "*.dll"))
{
    assemblyPath
});

using (var metadataContext = new MetadataLoadContext(resolver))
{
    Assembly assembly = metadataContext.LoadFromAssemblyPath(assemblyPath);

    foreach (var type in assembly.GetTypes())
    {
        if (type.IsPublic)
        {
            Console.WriteLine(type.Name);
        }
    }
}

This will print the names of all public types in the assembly:

MainClass
Startup
LoggerService
ILoggerService
LogRepository
LogMessage
LogController

While ReflectionOnlyLoadFrom() works well in .NET Framework, you'll need to rely on MetadataReader or MetadataLoadContext in .NET Core for reflection-only loading. Each of these techniques provides a way to inspect assemblies for type metadata without fully loading them into memory, avoiding the potential for runtime errors.