Мне нужно каким-то образом получить имя типа, когда type.IsGenericType
= true
.
Type t = typeof(List<String>);
MessageBox.Show( ..?.. );
Что я хочу, это окно сообщения, в котором отображается List
... как я могу это сделать?
Мне нужно каким-то образом получить имя типа, когда type.IsGenericType
= true
.
Type t = typeof(List<String>);
MessageBox.Show( ..?.. );
Что я хочу, это окно сообщения, в котором отображается List
... как я могу это сделать?
Type t = ...;
if (t.IsGenericType)
{
Type g = t.GetGenericTypeDefinition();
MessageBox.Show(g.Name); // displays "List`1"
MessageBox.Show(g.Name.Remove(g.Name.IndexOf('`'))); // displays "List"
}
Вы можете реализовать метод расширения, чтобы получить "понятное имя" типа, например так:
public static class TypeNameExtensions
{
public static string GetFriendlyName(this Type type)
{
string friendlyName = type.Name;
if (type.IsGenericType)
{
int iBacktick = friendlyName.IndexOf(''');
if (iBacktick > 0)
{
friendlyName = friendlyName.Remove(iBacktick);
}
friendlyName += "<";
Type[] typeParameters = type.GetGenericArguments();
for (int i = 0; i < typeParameters.Length; ++i)
{
string typeParamName = GetFriendlyName(typeParameters[i]);
friendlyName += (i == 0 ? typeParamName : "," + typeParamName);
}
friendlyName += ">";
}
return friendlyName;
}
}
С этим в вашем проекте, теперь вы можете сказать:
MessageBox.Show(t.GetFriendlyName());
И он будет отображать "Список <String>".
Я знаю, что OP не запрашивал параметры универсального типа, но я предпочитаю его таким образом. ;-)
Пространства имен, стандартные псевдонимы для встроенных типов и использование StringBuilder оставлены в качестве упражнения для читателя. ;-)
Мой подход к йойо. Обеспечивает более дружественные имена для примитивов, обрабатывает массивы и рекурсивно обрабатывает вложенные дженерики. Также модульные тесты.
private static readonly Dictionary<Type, string> _typeToFriendlyName = 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)
{
string friendlyName;
if (_typeToFriendlyName.TryGetValue(type, out friendlyName))
{
return friendlyName;
}
friendlyName = type.Name;
if (type.IsGenericType)
{
int backtick = friendlyName.IndexOf('`');
if (backtick > 0)
{
friendlyName = friendlyName.Remove(backtick);
}
friendlyName += "<";
Type[] typeParameters = type.GetGenericArguments();
for (int i = 0; i < typeParameters.Length; i++)
{
string typeParamName = typeParameters[i].GetFriendlyName();
friendlyName += (i == 0 ? typeParamName : ", " + typeParamName);
}
friendlyName += ">";
}
if (type.IsArray)
{
return type.GetElementType().GetFriendlyName() + "[]";
}
return friendlyName;
}
[TestFixture]
public class TypeHelperTest
{
[Test]
public void TestGetFriendlyName()
{
Assert.AreEqual("string", typeof(string).FriendlyName());
Assert.AreEqual("int[]", typeof(int[]).FriendlyName());
Assert.AreEqual("int[][]", typeof(int[][]).FriendlyName());
Assert.AreEqual("KeyValuePair<int, string>", typeof(KeyValuePair<int, string>).FriendlyName());
Assert.AreEqual("Tuple<int, string>", typeof(Tuple<int, string>).FriendlyName());
Assert.AreEqual("Tuple<KeyValuePair<object, long>, string>", typeof(Tuple<KeyValuePair<object, long>, string>).FriendlyName());
Assert.AreEqual("List<Tuple<int, string>>", typeof(List<Tuple<int, string>>).FriendlyName());
Assert.AreEqual("Tuple<short[], string>", typeof(Tuple<short[], string>).FriendlyName());
}
}
Предполагая, что вам просто нужно увидеть, что его List<T>
вместо List<string>
вам нужно будет сделать:
MessageBox.Show(t.GetGenericTypeDefinition().FullName)
См. http://msdn.microsoft.com/en-us/library/system.type.getgenerictypedefinition.aspx
public static class TypeNameExtensions
{
public static string GetFriendlyName(this Type type)
{
var friendlyName = type.Name;
if (!type.IsGenericType) return friendlyName;
var iBacktick = friendlyName.IndexOf('`');
if (iBacktick > 0) friendlyName = friendlyName.Remove(iBacktick);
var genericParameters = type.GetGenericArguments().Select(x => x.GetFriendlyName());
friendlyName += "<" + string.Join(", ", genericParameters) + ">";
return friendlyName;
}
}
Вот мой пример. Я не поставил контрольную подсказку, потому что, что я вижу, она всегда там. Вы можете добавить его, если хотите, но мне нравится делать вещи простыми.
public static string GetFriendlyName(this Type type)
{
if (type.IsGenericType)
{
var name = type.Name.Substring(0, type.Name.IndexOf('`'));
var types = string.Join(",", type.GetGenericArguments().Select(GetFriendlyName));
return $"{name}<{types}>";
}
else
{
return type.Name;
}
}
У меня улучшена версия yoyos для использования в Code Generation. Обратите внимание, что теперь все типы ссылаются на полную квалификацию = > global:: System.String.
public static string GetFriendlyTypeName(Type type)
{
string friendlyName = type.Name;
if (type.IsGenericType)
{
int iBacktick = friendlyName.IndexOf('`');
if (iBacktick > 0)
{
friendlyName = friendlyName.Remove(iBacktick);
}
friendlyName += "<";
Type[] typeParameters = type.GetGenericArguments();
for (int i = 0; i < typeParameters.Length; ++i)
{
string typeParamName = GetFriendlyTypeName(typeParameters[i]);
friendlyName += (i == 0 ? typeParamName : "," + typeParamName);
}
friendlyName += ">";
friendlyName = "global::" + type.Namespace + "." + friendlyName;
}
else
{
friendlyName = "global::" + type.FullName;
}
return friendlyName.Replace('+', '.');
}
Я знаю, что это старый вопрос, но мне и коллеге нужно было сделать это для какой-то интеллигентной/розлинской работы. Оптимальным решением оказалось решение Али, но оно не работает для вложенных типов:
int i = 1; //would work
List<string> listTest = new List<string>(); //would work
Dictionary<string, int> dictTest = new Dictionary<string, int>(); //would work
Dictionary<int, List<string>> nestTest = new Dictionary<int, List<string>>(); //would fail
Dictionary<int, List<Dictionary<string, List<object>>>> superNestTest = new Dictionary<int, List<Dictionary<string, List<object>>>>(); //would fail
Dictionary<int, List<Dictionary<string, int>>> superNestTest2 = new Dictionary<int, List<Dictionary<string, int>>>(); //would fail
Чтобы решить эти проблемы, я преобразовал функцию в рекурсивный метод:
public static class TypeExtensions
{
public static string GetFriendlyName(this Type type)
{
string friendlyName = type.FullName;
if (type.IsGenericType)
{
friendlyName = GetTypeString(type);
}
return friendlyName;
}
private static string GetTypeString(Type type)
{
var t = type.AssemblyQualifiedName;
var output = new StringBuilder();
List<string> typeStrings = new List<string>();
int iAssyBackTick = t.IndexOf(''') + 1;
output.Append(t.Substring(0, iAssyBackTick - 1).Replace("[", string.Empty));
var genericTypes = type.GetGenericArguments();
foreach (var genType in genericTypes)
{
typeStrings.Add(genType.IsGenericType ? GetTypeString(genType) : genType.ToString());
}
output.Append($"<{string.Join(",", typeStrings)}>");
return output.ToString();
}
}
выполнение предыдущих примеров/тестовых примеров дало следующие результаты:
System.Int32
System.Collections.Generic.List<System.String>
System.Collections.Generic.Dictionary<System.String,System.Int32>
System.Collections.Generic.Dictionary<System.Int32,System.Collections.Generic.List<System.String>>
System.Collections.Generic.Dictionary<System.Int32,System.Collections.Generic.List<System.Collections.Generic.Dictionary<System.String,System.Collections.Generic.List<System.Object>>>>
System.Collections.Generic.Dictionary<System.Int32,System.Collections.Generic.List<System.Collections.Generic.Dictionary<System.String,System.Int32>>>
Я потратил некоторое время, пытаясь решить проблему с вложенными типами, поэтому хотел документировать это здесь, чтобы кто-нибудь еще в будущем мог сэкономить немало времени (и головных болей!). Я также проверил производительность, и это в микросекундах для завершения (8 микросекунд в случае последнего сценария:
Результаты производительности
(Имена переменных, используемые из оригинального списка сценариев)"i" | 43uS
"listTest" | 3uS
"dictTest" | 2uS
"nestTest" | 5uS
"superNestTest" | 9uS
"superNestTest2" | 9uS
Среднее количество раз после выполнения вышеуказанного кода 200 раз для каждого сценария