当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<String>"。
我知道OP没有要求泛型类型参数,但我更喜欢这种方式。;-)
命名空间,内置类型的标准别名和使用StringBuilder留给读者练习。;-)
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.GetGenericArguments()[0].Name
的代码。我以前有一次需要用到这个,但无法在任何地方找到它。如果您有一个 List<string>
类型,这将返回字符串 "string"
。 - Felipe Correa我对 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() + "?";
- Igor S[]
括号。 GetFriendlyName(typeof(Foo<Bar<Bas>[]>))
返回 Foo<Bar<Bas>>
。 - AnorZakentype.IsArray
和 type.GetElementType()
来解决数组的别名。我已经更新了我的答案,展示了如何做到这一点 有关类型别名的问题。 - AnorZaken假设你只是想看到它是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提供的方案,但它对于嵌套类型不起作用:
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>>>
以下是基于之前答案的完整实现,支持别名(包括可空别名)和数组:
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
类型。
我已经改进了yoyo版本用于代码生成,值得注意的是现在所有类型都是全限定名 => 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('+', '.');
}
var s = x.GetType().ShortDisplayName();
它会返回Thing<IFoo>
编辑:抱歉,这只适用于EF Core的扩展。 :(
friendlyName += $"<{string.Join(",", type.GetGenericArguments().Select(p => type.GetFriendlyName()))}>"
其中,type.GetGenericArguments()
返回泛型类型参数数组;Select()
对每个泛型类型参数应用type.GetFriendlyName()
方法;string.Join()
使用逗号将结果连接成一个字符串。最终的结果会添加到friendlyName
中。 - joshcomleystring typeParamName = typeParameters[i].GetFriendlyName();
- PeterBMyType`1[]
,因此将使用friendlyName.Remove(iBacktick);
删除尾部数组括号[]
。您需要使用type.IsArray
进行测试,并使用type.GetArrayRank()
确定数组具有多少维。 - AnorZakenSelect
中使用type
而不是p
,那么你会让堆栈感到不高兴。 ;) - AnorZaken