I'm writing a code generation library, I've exposed a couple of methods and of course in order to define a member you need to specify it's type, the easiest way is to use typeof(Type)
, which I later format so that it it's compilable and I can insert it as a code chunk e.g. System.Int32
, which for non-generic types is basically concatenating the namespace and the type's name.
That works fine but generic types have the backtick followed by the number of arguments, so I needed a method to format all the types, so that they are always compilable e.g. System.Collection.Generics.List<System.Int32>
.
public static class TypeExtensions
{
public static string FormatTypeName(this Type type)
{
if(type == null) { throw new ArgumentNullException(nameof(type)); }
if (type.IsGenericType)
{
var definedArguments = type.GetGenericArguments();
var genericIdentifier = $"`{definedArguments.Length}";
if (definedArguments.First().FullName == null)
{
var genericBraces = "<".PadRight(definedArguments.Length, ',') + ">";
return $"{type.Namespace}.{type.Name.Replace(genericIdentifier, genericBraces)}";
}
var formattedTypes = definedArguments.Select(definedArgument => definedArgument.FormatTypeName());
var newGenericArguments = $"<{string.Join(", ", formattedTypes)}>";
return $"{type.Namespace}.{type.Name.Replace(genericIdentifier, newGenericArguments, StringComparison.InvariantCultureIgnoreCase)}";
}
return $"{type.Namespace}.{type.Name}";
}
}
It supports indefinite amount of nested and linear generic arguments due to the recursion that is in-place.
The way it works is pretty straight forward, there are 2 possible incomes, we have no arguments defined e.g. typeof(Dictionary<,>)
or all of the arguments are defined e.g typeof(Dictionary<int, int>)
.
Since the C# compiler doesn't allow partially defined arguments we can simply check if the first of the arguments has a FullName (note that Name
is the name pf the generic argument not the type behind it), if it does we simply concatenate all of the arguments and surround them by <>
. if the first argument doesn't have value then it's empty and we just leave the braces with commas separating the arguments.
Since this will be used in a code generation tool, performance is not crucial, but always welcome as long as it doesn't damage the readability and maintainability of the code, which I consider more important in this case.
However since it will be used in a code generation tool, I'm more worried about whether I've missed some edge cases.
ExtClass.ExtMethod(obj)
, it's not a member call onobj
;-) \$\endgroup\$