There are such properties:

MyClass Field1 { get; set; } List<MyClass> Field2 { get; set; } int? Field3 { get; set; } 

I have a PropertyInfo. How can I extract / build strings of the original types of these properties? By source, I mean exactly "MyClass" , "List<MyClass>" and "int?" If I use nameof and PropertyType.Name, I get a short entry from generics:

 MyClass List`1 Nullable`1 

Maybe there are some built-in tools that allow you to build the original type name string? (I really do not want to do these conversions manually)

  • Not so simple. The fact is that .NET is not limited to C # language alone. These types are named differently in different languages ​​of the platform, so the framework cannot give you a name as it is written in your language, it does not even know what language module is currently being executed! - VladD
  • @VladD, Do you want to say that in any case, it will be easier to write a self-made transformation? - test123
  • 2
    I'm afraid that's how it is - VladD
  • one
    @VladD, Thank you so much for saving me time). If someone needs an answer to this question, I found a similar one on the en stack: stackoverflow.com/a/33529925/4401952 Why I didn't google it before asking this question - I don't understand ... (facepalm) Yes, sugar solutions are not sort of? not there, but, you can recover from a Nullable wrapper, if desired. (you can see the comment to the answer from Igor S ) - test123
  • Yeah, as well as for new-fashioned tuples (ValueTuple) and for every other sugar as it appears (Ranges, for example) - Andrey NOP

1 answer 1

See, .NET is not limited to just C #. There are aliases in C #, which may not be in other .NET languages. Then, the recording format of generic types in (for example) VB is completely different. Therefore, the framework can not give you a name as it is written in C #, because it does not even know what language module is currently being executed!

Therefore, without a "manual" conversion is not enough.


Slightly expanded the solution mentioned in the comments : added Nullable , tuples (stupid), as well as “open” generic types. Limitations of this method: the names of the elements of the tuples, as well as dynamic , are not detected, since the information about this is encoded not in the type, but in the attribute hung on the field.

 public static class TypeExtensions { private static readonly Dictionary<Type, string> typeToAlias = new Dictionary<Type, string> { { typeof(string), "string" }, { typeof(object), "object" }, { typeof(bool), "bool" }, { typeof(byte), "byte" }, { typeof(char), "char" }, { typeof(decimal), "decimal" }, { typeof(double), "double" }, { typeof(short), "short" }, { typeof(int), "int" }, { typeof(long), "long" }, { typeof(sbyte), "sbyte" }, { typeof(float), "float" }, { typeof(ushort), "ushort" }, { typeof(uint), "uint" }, { typeof(ulong), "ulong" }, { typeof(void), "void" } }; public static string GetFriendlyName(this Type type) { if (typeToAlias.TryGetValue(type, out var reservedName)) return reservedName; if (type.IsArray) return type.GetElementType().GetFriendlyName() + "[]"; var nullableBase = Nullable.GetUnderlyingType(type); if (nullableBase != null) return nullableBase.GetFriendlyName() + "?"; var tupleTypes = GetTupleTypes(type); if (tupleTypes != null) { var tupleTypeNames = tupleTypes.Select(t => t.GetFriendlyName()); return "(" + string.Join(", ", tupleTypeNames) + ")"; } var friendlyName = type.Name; if (type.IsGenericType) { int backtick = friendlyName.IndexOf('`'); if (backtick > 0) friendlyName = friendlyName.Remove(backtick); var genericArgs = type.GetGenericArguments(); if (type.IsGenericTypeDefinition) friendlyName += "<" + new string(',', genericArgs.Length - 1) + ">"; else friendlyName += "<" + string.Join(", ", genericArgs.Select(t => t.GetFriendlyName())) + ">"; } return friendlyName; } static readonly HashSet<Type> tupleTypes = new HashSet<Type>() { typeof(ValueTuple<>), typeof(ValueTuple<,>), typeof(ValueTuple<,,>), typeof(ValueTuple<,,,>), typeof(ValueTuple<,,,,>), typeof(ValueTuple<,,,,,>), typeof(ValueTuple<,,,,,,>) }; static IEnumerable<Type> GetTupleTypes(Type type) { if (type == typeof(ValueTuple)) return Enumerable.Empty<Type>(); if (!type.IsGenericType) return null; var def = type.GetGenericTypeDefinition(); if (tupleTypes.Contains(def)) return type.GetGenericArguments(); if (def == typeof(ValueTuple<,,,,,,,>)) { var args = type.GetGenericArguments(); var lastArg = args[args.Length - 1]; var restTypes = GetTupleTypes(lastArg); if (restTypes != null) return args.Take(args.Length - 1).Concat(restTypes); } return null; } } 
  • Perfect solution! Thanks for the answer. - test123
  • @ test123: Please! - VladD