A .NET library that converts reflection types (PropertyInfo, ParameterInfo, FieldInfo, EventInfo) into human-readable type strings. Handles nullable reference types, generics, arrays, and C# type aliases with ease.
$ dotnet add package Porticle.Reflection.ExtensionsA .NET library that provides extension methods for converting .NET reflection types into human-readable type strings. This library handles complex scenarios including nullable reference types, nullable value types, generic types, arrays, and C# type aliases, making reflection output easier to read and understand.
For example, when you have a Propterty of type like IDictionary<MyClass?[][], Dictionary<object?,int?[,]?>>?, the library is able to convert it to a readable c# type name that looks mostly exact like the normal c# type name in most cases.
When you use .ToString() you will get
System.Collections.Generic.IDictionary`2[MyClass[][],System.Collections.Generic.Dictionary`2[System.Object,System.Nullable`1[System.Int32][,]]]
When you use this library you will get
IDictionary<MyClass?[][], Dictionary<object?,int?[,]?>>?
The library automatically converts reflection types to readable strings:
string?, List<string>?)int?, DateTime?)List<T>, Dictionary<TKey, TValue>)[], jagged [][], and multi-dimensional [,])int, string, bool instead of Int32, String, Boolean)EventHandler<T>, Action<T>, Func<T>)List<List<string?>>?)Install via NuGet:
dotnet add package Porticle.Reflection.Extensions
Or via Package Manager Console:
Install-Package Porticle.Reflection.Extensions
using Porticle.Reflection.Extensions;
// Simple types
Type intType = typeof(int);
Console.WriteLine(intType.ToReadableTypeString()); // Output: int
Type stringType = typeof(string);
Console.WriteLine(stringType.ToReadableTypeString()); // Output: string
// Nullable value types
Type nullableInt = typeof(int?);
Console.WriteLine(nullableInt.ToReadableTypeString()); // Output: int?
// Generic types
Type listType = typeof(List<string>);
Console.WriteLine(listType.ToReadableTypeString()); // Output: List<string>
Type dictType = typeof(Dictionary<int, string>);
Console.WriteLine(dictType.ToReadableTypeString()); // Output: Dictionary<int, string>
public class User
{
public string Name { get; set; } = "";
public string? Email { get; set; }
public List<string>? Tags { get; set; }
public List<string?> Nicknames { get; set; } = new();
}
// Using PropertyInfo to get nullable reference type information
PropertyInfo nameProperty = typeof(User).GetProperty("Name")!;
Console.WriteLine(nameProperty.ToReadableTypeString()); // Output: string
PropertyInfo emailProperty = typeof(User).GetProperty("Email")!;
Console.WriteLine(emailProperty.ToReadableTypeString()); // Output: string?
PropertyInfo tagsProperty = typeof(User).GetProperty("Tags")!;
Console.WriteLine(tagsProperty.ToReadableTypeString()); // Output: List<string>?
PropertyInfo nicknamesProperty = typeof(User).GetProperty("Nicknames")!;
Console.WriteLine(nicknamesProperty.ToReadableTypeString()); // Output: List<string?>
public class DataService
{
public void ProcessData(
List<string> requiredItems,
List<string>? optionalItems,
List<string?> itemsWithNullableElements)
{
}
}
MethodInfo method = typeof(DataService).GetMethod("ProcessData")!;
ParameterInfo[] parameters = method.GetParameters();
Console.WriteLine(parameters[0].ToReadableTypeString()); // Output: List<string>
Console.WriteLine(parameters[1].ToReadableTypeString()); // Output: List<string>?
Console.WriteLine(parameters[2].ToReadableTypeString()); // Output: List<string?>
public class EventDemo
{
public List<string>? Items;
public event EventHandler<string>? DataReceived;
}
// Fields
FieldInfo field = typeof(EventDemo).GetField("Items")!;
Console.WriteLine(field.ToReadableTypeString()); // Output: List<string>?
// Events
EventInfo evt = typeof(EventDemo).GetEvent("DataReceived")!;
Console.WriteLine(evt.ToReadableTypeString()); // Output: EventHandler<string>?
Use useFullNames: true to include full namespaces:
Type listType = typeof(List<string>);
Console.WriteLine(listType.ToReadableTypeString(useFullNames: true));
// Output: System.Collections.Generic.List<System.String>
Use useInternalTypeNames: false to get .NET type names instead of C# aliases:
Type intType = typeof(int);
Console.WriteLine(intType.ToReadableTypeString(useInternalTypeNames: false));
// Output: Int32
Type stringType = typeof(string);
Console.WriteLine(stringType.ToReadableTypeString(useInternalTypeNames: false));
// Output: String
// Single-dimensional array
Type intArray = typeof(int[]);
Console.WriteLine(intArray.ToReadableTypeString()); // Output: int[]
// Jagged array
Type jaggedArray = typeof(int[][]);
Console.WriteLine(jaggedArray.ToReadableTypeString()); // Output: int[][]
// Multi-dimensional array
Type multiArray = typeof(int[,]);
Console.WriteLine(multiArray.ToReadableTypeString()); // Output: int[,]
Due to .NET's type erasure for nullable reference types, typeof() cannot preserve nullability information for generic type arguments:
// ❌ This will NOT show the nullable string
Type listType = typeof(List<string?>);
Console.WriteLine(listType.ToReadableTypeString()); // Output: List<string> (nullability lost)
// ✅ Use PropertyInfo, ParameterInfo, FieldInfo, or EventInfo instead
public class Example
{
public List<string?> Items { get; set; } = new();
}
PropertyInfo property = typeof(Example).GetProperty("Items")!;
Console.WriteLine(property.ToReadableTypeString()); // Output: List<string?> (correct)
Note: Nullable value types (like List<int?>) work correctly with typeof() because Nullable<T> is a real runtime type.
The library is optimized for performance with:
Contributions are welcome! Please feel free to submit a Pull Request. For major changes, please open an issue first to discuss what you would like to change.
dotnet restore in the Source directorydotnet build to build the projectdotnet test to run all testsThis project uses an .editorconfig file to maintain consistent code style. Please ensure your IDE respects these settings.
# Run all tests
dotnet test
# Run with code coverage
dotnet test --collect:"XPlat Code Coverage"
# Run a specific test
dotnet test --filter "FullyQualifiedName~ToReadableTypeString_IntType"
See CHANGELOG.md for a list of changes in each version.
MIT License - see LICENSE file for details.