I recently needed to build C# specific name (which must always include global:: specifier) for an arbitrary type and have come accross following issue:
// 1 - value: System.String[,,,][,,][,]
string unexpectedFullName = typeof( string[,][,,][,,,] ).FullName;
// 2 - value: System.String[,][,,][,,,]
string expectedFullName = Type.GetType( "System.String[,][,,][,,,]" ).FullName;
I was expecting that returned value would be same in both cases. However, for some reason the array related part of the value seems to be reversed (case 1). Is this reversal expected behavior?
While the value returned by Type.FullName
and the C# type identifier sometimes happen to be the same, this is not guaranteed. Keep in mind that Type.FullName
returns the same value regardless of what CLI language it is called from, be it C#, VB.NET, Oxygene or anything else.
For multidimensional and jagged arrays, C# syntax lists the indices in the order they are written later on, while reflection syntax returns something that matches the logical structure of the array. And a (C#) string[,][,,][,,,]
is, after all, a value of type string
, thereof a 4-dimensional array (i.e. string[,,,]
), thereof a 3-dimensional array (i.e. string[,,,][,,]
) and thereof a 2-dimensional array (i.e. string[,,,][,,][,]
).
Rather than relying on the reflection syntax name returned by FullName
, you might want to examine the properties of the Type
class when analyzing types. Information such as the number of dimensions or the generic arguments can be retrieved from there.
When constructing types, you can also use methods such as MakeArrayType
or MakeGenericType
to create complex types at runtime without constructing a string that contains the ingredients for the new types.
Some of the contents of this answer was pointed out by Marc Gravell - thank you!
Note: This doesn't directly address your question
Is this reversal expected behavior?
but I feel it adds to it.
You can use GenerateCodeFromExpression
to return a string which could be used to generate the code to generate the type for you, for instance using this code (modified from this SO answer by hvd):
/// <summary>
/// <para>Returns a readable name for this type.</para>
/// <para>e.g. for type = typeof(IEnumerable<IComparable<int>>),</para>
/// <para>type.FriendlyName() returns System.Collections.Generic.IEnumerable<System.IComparable<int>></para>
/// <para>type.Name returns IEnumerable`1</para>
/// <para>type.FullName() returns System.Collections.Generic.IEnumerable`1[[System.IComparable`1[[System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]</para>
/// </summary>
public static string FriendlyName(this Type type)
{
string result;
using ( var codeDomProvider = CodeDomProvider.CreateProvider("C#") )
{
var typeReferenceExpression = new CodeTypeReferenceExpression(new CodeTypeReference(type));
using ( var writer = new StringWriter() )
{
codeDomProvider.GenerateCodeFromExpression(typeReferenceExpression, writer, new CodeGeneratorOptions());
result = writer.GetStringBuilder().ToString();
}
}
return result;
}
By letting codeDomProvider
handle the string representation, you can be sure that what's generated will match how you'd define the Type.
Results with FullName
:
// returns "System.String[,,,][,,][,]"
typeof(string[,][, ,][, , ,]).FullName;
// returns "System.String[,][,,][,,,]"
typeof(string[, , ,][, ,][,]).FullName;
// returns "System.Collections.Generic.List`1[[System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]"
typeof(List<int>).FullName;
Results with FriendlyName
// returns "string[,][,,][,,,]"
typeof(string[,][, ,][, , ,]).FriendlyName();
// returns "string[,,,][,,][,]"
typeof(string[, , ,][, ,][,]).FriendlyName();
// returns "System.Collections.Generic.List<int>"
typeof(List<int>).FriendlyName();
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With