【发布时间】:2010-11-15 14:54:26
【问题描述】:
当type.IsGenericType = true 时,我需要一些方法来获取类型的名称。
Type t = typeof(List<String>);
MessageBox.Show( ..?.. );
我想要的是弹出一个带有List 的消息框,显示...我该怎么做?
【问题讨论】:
当type.IsGenericType = true 时,我需要一些方法来获取类型的名称。
Type t = typeof(List<String>);
MessageBox.Show( ..?.. );
我想要的是弹出一个带有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());
它会显示“List
我知道 OP 没有要求泛型类型参数,但我更喜欢这种方式。 ;-)
命名空间、standard aliases for built-in types 和 StringBuilder 的使用留给读者作为练习。 ;-)
【讨论】:
friendlyName += $"<{string.Join(",", type.GetGenericArguments().Select(p => type.GetFriendlyName()))}>"
string typeParamName = typeParameters[i].GetFriendlyName();
MyType`1[],因此数组尾括号 [] 将被 friendlyName.Remove(iBacktick); 删除。您需要使用type.IsArray 对此进行测试,并使用type.GetArrayRank() 确定数组有多少维。
Select 中使用type 而不是p,你会让堆栈不愉快。 ;)
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"
}
【讨论】:
List<T>这样的泛型类型T,你可以使用t.GetGenericArguments()[0].Name这样的东西。不久前我需要这个,但在任何地方都找不到。如果您有List<string>,这将返回string
我对 yoyo 方法的看法。确保原语的名称更友好,处理数组并且递归处理嵌套的泛型。还有单元测试。
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());
}
}
【讨论】:
if (type.GetGenericTypeDefinition() == typeof(Nullable<>)) return type.GetGenericArguments().First().GetFriendlyName() + "?";
[] 括号。 GetFriendlyName(typeof(Foo<Bar<Bas>[]>)) 返回Foo<Bar<Bas>>。
type.IsArray 和 type.GetElementType() 解析数组的别名。我更新了我的回答 about type aliases 以展示如何做到这一点。
假设您只想看到它的 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;
}
}
【讨论】:
我知道这是一个老问题,但我和一位同事需要为一些智能感知/罗斯林工作做这个。最佳解决方案似乎是Ali's 解决方案,但它不适用于嵌套类型:
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 次后的平均次数
【讨论】:
这是基于之前支持别名(包括 Nullable)和数组的答案的完整实现:
public static class TypeNameExtensions
{
public static string GetFriendlyName(this Type type, bool aliasNullable = true, bool includeSpaceAfterComma = true)
{
TryGetInnerElementType(ref type, out string arrayBrackets);
if (!TryGetNameAliasNonArray(type, out string friendlyName))
{
if (!type.IsGenericType)
{
friendlyName = type.Name;
}
else
{
if (aliasNullable && type.GetGenericTypeDefinition() == typeof(System.Nullable<>))
{
string generics = GetFriendlyName(type.GetGenericArguments()[0]);
friendlyName = generics + "?";
}
else
{
string generics = GetFriendlyGenericArguments(type, includeSpaceAfterComma);
int iBacktick = type.Name.IndexOf('`');
friendlyName = (iBacktick > 0 ? type.Name.Remove(iBacktick) : type.Name)
+ $"<{generics}>";
}
}
}
return friendlyName + arrayBrackets;
}
public static bool TryGetNameAlias(this Type type, out string alias)
{
TryGetInnerElementType(ref type, out string arrayBrackets);
if (!TryGetNameAliasNonArray(type, out alias))
return false;
alias += arrayBrackets;
return true;
}
private static string GetFriendlyGenericArguments(Type type, bool includeSpaceAfterComma)
=> string.Join(
includeSpaceAfterComma ? ", " : ",",
type.GetGenericArguments().Select(t => t.GetFriendlyName())
);
private static bool TryGetNameAliasNonArray(Type type, out string alias)
=> (alias = TypeAliases[(int)Type.GetTypeCode(type)]) != null
&& !type.IsEnum;
private static bool TryGetInnerElementType(ref Type type, out string arrayBrackets)
{
arrayBrackets = null;
if (!type.IsArray)
return false;
do
{
arrayBrackets += "[" + new string(',', type.GetArrayRank() - 1) + "]";
type = type.GetElementType();
}
while (type.IsArray);
return true;
}
private static readonly string[] TypeAliases = {
"void", // 0
null, // 1 (any other type)
"DBNull", // 2
"bool", // 3
"char", // 4
"sbyte", // 5
"byte", // 6
"short", // 7
"ushort", // 8
"int", // 9
"uint", // 10
"long", // 11
"ulong", // 12
"float", // 13
"double", // 14
"decimal", // 15
null, // 16 (DateTime)
null, // 17 (-undefined-)
"string", // 18
};
}
用废话测试如:
var type = typeof(Dictionary<string[,], List<int?[,][]>[,,]>[]);
var name = type.GetFriendlyName();
Console.WriteLine(name);
它确实返回:"Dictionary<string[,], List<int?[,][]>[,,]>[]"
编辑:已更新以正确处理 enum 类型。
【讨论】:
我已经改进了 yoyos 版本,以便在代码生成中使用。 请注意,所有类型现在都引用完全限定 => 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('+', '.');
}
【讨论】: