TypeQualifications: Type Name Formatting Control¶
Verified by tests
TypeQualificationTests, TypeFormatterTests — library CI run #31657041675 (2026-08-13)
TypeQualifications is a flag enum that controls how .NET type names are formatted in generated code. It enables fine-grained control over namespace, assembly, version, and other type name components. (The enum name is plural — TypeQualifications — while its source file is TypeQualification.cs.)
Overview¶
TypeQualifications provides: - ✅ Flag-based control over type name components - ✅ Individual component flags for fine-grained control - ✅ Composite presets for common scenarios - ✅ Fully AOT-compatible (no reflection) - ✅ Used by source generators and message association APIs
Quick Start¶
Using TypeQualifications Flags¶
Using TypeQualifications Flags
using Whizbang.Core;
var type = typeof(ECommerce.Contracts.Events.ProductCreatedEvent);
// Simple type name only
var simple = TypeFormatter.FormatType(type, TypeQualifications.Simple);
// Result: "ProductCreatedEvent"
// Namespace + type name
var namespaced = TypeFormatter.FormatType(type, TypeQualifications.NamespaceQualified);
// Result: "ECommerce.Contracts.Events.ProductCreatedEvent"
// Namespace + type + assembly
var fullyQualified = TypeFormatter.FormatType(type, TypeQualifications.FullyQualified);
// Result: "ECommerce.Contracts.Events.ProductCreatedEvent, ECommerce.Contracts"
// With version information
var withVersion = TypeFormatter.FormatType(type, TypeQualifications.FullyQualifiedWithVersion);
// Result: "ECommerce.Contracts.Events.ProductCreatedEvent, ECommerce.Contracts, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"
Combining Individual Flags¶
Combining Individual Flags
// Custom combination: Namespace + Type + Assembly (no version)
var custom = TypeFormatter.FormatType(
type,
TypeQualifications.Namespace | TypeQualifications.TypeName | TypeQualifications.Assembly
);
// Result: "ECommerce.Contracts.Events.ProductCreatedEvent, ECommerce.Contracts"
// With global prefix
var globalQualified = TypeFormatter.FormatType(
type,
TypeQualifications.GlobalPrefix | TypeQualifications.Namespace | TypeQualifications.TypeName
);
// Result: "global::ECommerce.Contracts.Events.ProductCreatedEvent"
Component Flags¶
Individual Component Flags¶
Each flag controls a specific part of the type name:
| Flag | Value | Description |
|---|---|---|
None |
0 | Empty string (no components) |
TypeName |
1 | Type name only (e.g., "ProductCreatedEvent") |
Namespace |
2 | Namespace prefix (e.g., "ECommerce.Contracts.Events") |
Assembly |
4 | Assembly name (e.g., "ECommerce.Contracts") |
Version |
8 | Assembly version (e.g., "Version=1.0.0.0") |
Culture |
16 | Culture info (e.g., "Culture=neutral") |
PublicKeyToken |
32 | Public key token (e.g., "PublicKeyToken=null") |
GlobalPrefix |
64 | Global namespace prefix (e.g., "global::") |
Example - Combining Flags: Individual Component Flags
// Just namespace and type name
var flags = TypeQualifications.Namespace | TypeQualifications.TypeName;
var result = TypeFormatter.FormatType(typeof(OrderCreatedEvent), flags);
// Result: "MyApp.Events.OrderCreatedEvent"
// Type name with global prefix
var globalFlags = TypeQualifications.GlobalPrefix | TypeQualifications.TypeName;
var globalResult = TypeFormatter.FormatType(typeof(OrderCreatedEvent), globalFlags);
// Result: "global::OrderCreatedEvent"
Composite Presets¶
Pre-defined combinations for common scenarios:
| Preset | Flags | Example Output |
|---|---|---|
Simple |
TypeName |
"ProductCreatedEvent" |
NamespaceQualified |
Namespace \| TypeName |
"ECommerce.Contracts.Events.ProductCreatedEvent" |
AssemblyQualified |
TypeName \| Assembly |
"ProductCreatedEvent, ECommerce.Contracts" |
FullyQualified |
Namespace \| TypeName \| Assembly |
"ECommerce.Contracts.Events.ProductCreatedEvent, ECommerce.Contracts" |
GlobalQualified |
GlobalPrefix \| Namespace \| TypeName |
"global::ECommerce.Contracts.Events.ProductCreatedEvent" |
FullyQualifiedWithVersion |
All flags except GlobalPrefix |
"..., Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" |
Example - Using Presets: Composite Presets
var type = typeof(ProductCreatedEvent);
// Simple preset
var simple = TypeFormatter.FormatType(type, TypeQualifications.Simple);
// "ProductCreatedEvent"
// FullyQualified preset
var full = TypeFormatter.FormatType(type, TypeQualifications.FullyQualified);
// "ECommerce.Contracts.Events.ProductCreatedEvent, ECommerce.Contracts"
// GlobalQualified preset
var global = TypeFormatter.FormatType(type, TypeQualifications.GlobalQualified);
// "global::ECommerce.Contracts.Events.ProductCreatedEvent"
Common Scenarios¶
Scenario 1: Source Generator Output¶
When: Generating code that references types
Scenario 1: Source Generator Output
// Generate code with global::-qualified type names (avoids namespace conflicts)
var messageType = TypeFormatter.FormatType(
typeof(OrderCreatedEvent),
TypeQualifications.GlobalQualified
);
var generatedCode = $@"
if (message is {messageType} orderCreated) {{
return HandleOrderCreated(orderCreated);
}}
";
// Output:
// if (message is global::ECommerce.Contracts.Events.OrderCreatedEvent orderCreated) {
// return HandleOrderCreated(orderCreated);
// }
Scenario 2: Message Association Lookup¶
When: Matching message types by name with different qualification levels
Scenario 2: Message Association Lookup
// Lookup by simple name
var associations = GetMessageAssociations(serviceName)
.Where(a => {
var simpleName = TypeFormatter.FormatType(
Type.GetType(a.MessageType)!,
TypeQualifications.Simple
);
return simpleName == "ProductCreatedEvent";
});
// Lookup by fully qualified name
var fullyQualifiedAssociations = GetMessageAssociations(serviceName)
.Where(a => {
var fullName = TypeFormatter.FormatType(
Type.GetType(a.MessageType)!,
TypeQualifications.FullyQualified
);
return fullName == "ECommerce.Contracts.Events.ProductCreatedEvent, ECommerce.Contracts";
});
Scenario 3: User-Facing Display¶
When: Showing type names in logs or UI
Scenario 3: User-Facing Display
public void LogEventProcessing(Type eventType) {
// Simple name for user-friendly display
var displayName = TypeFormatter.FormatType(eventType, TypeQualifications.Simple);
_logger.LogInformation("Processing event: {EventName}", displayName);
// Output: "Processing event: ProductCreatedEvent"
// Fully qualified for diagnostics
var fullName = TypeFormatter.FormatType(eventType, TypeQualifications.FullyQualified);
_logger.LogDebug("Full event type: {EventType}", fullName);
// Output: "Full event type: ECommerce.Contracts.Events.ProductCreatedEvent, ECommerce.Contracts"
}
Scenario 4: Version-Aware Type Matching¶
When: Matching types across different assembly versions
Scenario 4: Version-Aware Type Matching
// Format without version information
var typeWithoutVersion = TypeFormatter.FormatType(
type,
TypeQualifications.Namespace | TypeQualifications.TypeName | TypeQualifications.Assembly
);
// Format with version information
var typeWithVersion = TypeFormatter.FormatType(
type,
TypeQualifications.FullyQualifiedWithVersion
);
// Compare without version
bool matchesIgnoringVersion = TypeMatcher.Matches(
typeWithoutVersion,
"ECommerce.Contracts.Events.ProductCreatedEvent, ECommerce.Contracts",
MatchStrictness.Exact
);
Flag Enum Mechanics¶
Bitwise Operations¶
TypeQualifications uses the [Flags] attribute, enabling bitwise operations:
Bitwise Operations
// Check if a flag is set
bool hasNamespace = (qualification & TypeQualifications.Namespace) == TypeQualifications.Namespace;
// OR
bool hasNamespaceAlt = qualification.HasFlag(TypeQualifications.Namespace);
// Add a flag
var withAssembly = qualification | TypeQualifications.Assembly;
// Remove a flag
var withoutVersion = qualification & ~TypeQualifications.Version;
// Toggle a flag
var toggled = qualification ^ TypeQualifications.GlobalPrefix;
Building Qualification Dynamically¶
Building Qualification Dynamically
public TypeQualifications BuildQualification(
bool includeNamespace,
bool includeAssembly,
bool includeVersion) {
var result = TypeQualifications.TypeName; // Always include type name
if (includeNamespace) {
result |= TypeQualifications.Namespace;
}
if (includeAssembly) {
result |= TypeQualifications.Assembly;
}
if (includeVersion) {
result |= TypeQualifications.Version | TypeQualifications.Culture | TypeQualifications.PublicKeyToken;
}
return result;
}
// Usage
var qual = BuildQualification(
includeNamespace: true,
includeAssembly: true,
includeVersion: false
);
// Result: TypeQualifications.Namespace | TypeQualifications.TypeName | TypeQualifications.Assembly
Integration with TypeFormatter¶
TypeQualifications is designed to work seamlessly with TypeFormatter:
Integration with TypeFormatter
// TypeFormatter respects all flags
var formatted = TypeFormatter.FormatType(type, TypeQualifications.FullyQualified);
// Empty result for None
var empty = TypeFormatter.FormatType(type, TypeQualifications.None);
// Result: ""
// Handles combinations correctly
var custom = TypeFormatter.FormatType(
type,
TypeQualifications.GlobalPrefix | TypeQualifications.TypeName
);
// Result: "global::ProductCreatedEvent"
API Reference¶
Enum Definition¶
Namespace: Whizbang.Core
Enum Definition
[Flags]
public enum TypeQualifications {
None = 0,
// Component flags (individual bits)
TypeName = 1 << 0, // 1
Namespace = 1 << 1, // 2
Assembly = 1 << 2, // 4
Version = 1 << 3, // 8
Culture = 1 << 4, // 16
PublicKeyToken = 1 << 5, // 32
GlobalPrefix = 1 << 6, // 64
// Composite presets (combinations)
Simple = TypeName,
NamespaceQualified = Namespace | TypeName,
AssemblyQualified = TypeName | Assembly,
FullyQualified = Namespace | TypeName | Assembly,
GlobalQualified = GlobalPrefix | Namespace | TypeName,
FullyQualifiedWithVersion = Namespace | TypeName | Assembly | Version | Culture | PublicKeyToken
}
Usage with TypeFormatter¶
Usage with TypeFormatter
// Format a type with qualification
string formatted = TypeFormatter.FormatType(Type type, TypeQualifications qualification);
// Examples
var simple = TypeFormatter.FormatType(typeof(OrderCreatedEvent), TypeQualifications.Simple);
var full = TypeFormatter.FormatType(typeof(OrderCreatedEvent), TypeQualifications.FullyQualified);
var custom = TypeFormatter.FormatType(
typeof(OrderCreatedEvent),
TypeQualifications.Namespace | TypeQualifications.TypeName
);
Best Practices¶
- Use composite presets for common cases -
Simple,FullyQualified, etc. are easier to read - Combine individual flags for custom needs - Use bitwise OR for specific combinations
- Default to FullyQualified for generated code - Avoids ambiguity in generated source
- Use Simple for user-facing displays - More readable in logs and UI
- Consider IgnoreVersion for matching - Combine with MatchStrictness for flexible type matching
- Use GlobalPrefix in generated code - Avoids namespace conflicts with
global:: - Cache formatted results when possible - Formatting is deterministic, can be memoized
Common Pitfalls¶
❌ Forgetting TypeName Flag¶
❌ Forgetting TypeName Flag
// ❌ WRONG: Missing TypeName
var qual = TypeQualifications.Namespace | TypeQualifications.Assembly;
var result = TypeFormatter.FormatType(type, qual);
// Result: ", MyAssembly" - Invalid!
// ✅ CORRECT: Include TypeName
var qual = TypeQualifications.Namespace | TypeQualifications.TypeName | TypeQualifications.Assembly;
var result = TypeFormatter.FormatType(type, qual);
// Result: "MyNamespace.MyType, MyAssembly"
❌ Confusing Component Flags with Presets¶
❌ Confusing Component Flags with Presets
// ❌ WRONG: Trying to "remove" from a preset
var qualification = TypeQualifications.FullyQualified & ~TypeQualifications.Assembly;
// This works but is less clear
// ✅ CORRECT: Build from component flags
var qualification = TypeQualifications.Namespace | TypeQualifications.TypeName;
❌ Assuming Default Behavior¶
❌ Assuming Default Behavior
// ❌ WRONG: Assuming default includes namespace
var formatted = TypeFormatter.FormatType(type, TypeQualifications.TypeName);
// Result: "ProductCreatedEvent" - No namespace!
// ✅ CORRECT: Explicit about what you want
var formatted = TypeFormatter.FormatType(type, TypeQualifications.NamespaceQualified);
// Result: "ECommerce.Contracts.Events.ProductCreatedEvent"
See Also¶
- TypeFormatter - Formatting types according to qualification
- MatchStrictness - Fuzzy matching with type qualification
- TypeMatcher - Type matching utilities
- Perspectives - Using type qualification in message associations