This chapter explores advanced .NET Core features for performance optimization, extensibility, interop with native code, diagnostics, reflection, and source generators.
Modern .NET provides several high-performance APIs that minimize allocations and improve throughput.
Span<T> is a stack-allocated, ref-like type that provides safe access to contiguous memory without allocations. Memory<T> is its heap-compatible counterpart.
Span<byte> buffer = stackalloc byte[256];
ReadOnlySpan<char> text = "Hello".AsSpan();
int vowels = text.Count(SpanExtensions.IsVowel);
// Slicing without allocation
Span<byte> slice = buffer.Slice(10, 20);
A ref struct is a value type that lives only on the stack. It cannot be boxed or used as a field of a class.
public ref struct Matrix3x3
{
public float M11, M12, M13;
public float M21, M22, M23;
public float M31, M32, M33;
}
BenchmarkDotNet is the gold standard for microbenchmarking in .NET:
[MemoryDiagnoser]
public class StringBenchmarks
{
[Benchmark]
public string Concat() => string.Concat("a", "b", "c");
[Benchmark]
public string Interpolation() => $"{'a'}{'b'}{'c'}";
}
Managed Extensibility Framework (MEF) provides a plug-in architecture:
[Export(typeof(IPlugin))]
public class MyPlugin : IPlugin { }
// Composition
var catalog = new AssemblyCatalog(Assembly.GetExecutingAssembly());
var container = new CompositionContainer(catalog);
var plugins = container.GetExports<IPlugin>();
Custom MSBuild tasks let you extend the build pipeline:
public class MyTask : Microsoft.Build.Utilities.Task
{
public override bool Execute()
{
Log.LogMessage("Custom task running...");
return true;
}
}
P/Invoke calls native C functions:
[DllImport("user32.dll", CharSet = CharSet.Auto)]
public static extern int MessageBox(IntPtr hWnd, string text, string caption, uint type);
COM Interop allows consuming COM objects. Generate interop assemblies with tlbimp.exe or use dynamic with System.Runtime.InteropServices.
.NET Core includes powerful diagnostic tools:
dotnet tool install -g dotnet-counters
dotnet tool install -g dotnet-dump
dotnet tool install -g dotnet-trace
dotnet-counters monitor --process-id 1234
dotnet-dump collect --process-id 1234
dotnet-trace collect --providers Microsoft-DotNETCore-SampleProfiler
Use these tools to monitor CPU, memory, garbage collection, and thread pool metrics in production.
Reflection enables inspecting types, invoking members, and creating instances at runtime:
Assembly asm = Assembly.LoadFrom("MyLibrary.dll");
Type type = asm.GetType("MyLibrary.MyClass");
object obj = Activator.CreateInstance(type);
MethodInfo method = type.GetMethod("MyMethod");
method.Invoke(obj, new object[] { "arg" });
// Custom attributes
[AttributeUsage(AttributeTargets.Class)]
public class DisplayNameAttribute : Attribute
{
public string Name { get; }
public DisplayNameAttribute(string name) => Name = name;
}
Source generators produce C# source files during compilation. They enable compile-time code generation without reflection overhead.
[Generator]
public class HelloGenerator : ISourceGenerator
{
public void Execute(GeneratorExecutionContext context)
{
var source = "namespace Gen { public class Hello { public static string World() => \"Hello\"; } }";
context.AddSource("Hello.g.cs", source);
}
public void Initialize(GeneratorInitializationContext context) { }
}
[AutoLog] and generates a partial class with an ILogger property and a LogMethodEntry/Exit helper. Then use BenchmarkDotNet to compare the performance of string.Concat vs StringBuilder for 100 concatenations.