如何获得通用类型的正确名称?
例如: 这段代码
typeof(List<string>).Name
返回
List`1
而不是
List<string>
typeof(List<string>).ToString()
返回System.Collections.Generic.List`1 [System.String]但我想得到初始名称:
List<string>
真的吗?
答案 0 :(得分:34)
typeof(List<string>).FullName
这将为您提供名称空间+类+类型参数。
您要求的是C#特定语法。就.NET而言,这是正确的:
System.Collections.Generic.List`1[System.String]
所以为了获得你想要的东西,你必须编写一个函数来按照你想要的方式构建它。也许是这样:
static string GetCSharpRepresentation( Type t, bool trimArgCount ) {
if( t.IsGenericType ) {
var genericArgs = t.GetGenericArguments().ToList();
return GetCSharpRepresentation( t, trimArgCount, genericArgs );
}
return t.Name;
}
static string GetCSharpRepresentation( Type t, bool trimArgCount, List<Type> availableArguments ) {
if( t.IsGenericType ) {
string value = t.Name;
if( trimArgCount && value.IndexOf("`") > -1 ) {
value = value.Substring( 0, value.IndexOf( "`" ) );
}
if( t.DeclaringType != null ) {
// This is a nested type, build the nesting type first
value = GetCSharpRepresentation( t.DeclaringType, trimArgCount, availableArguments ) + "+" + value;
}
// Build the type arguments (if any)
string argString = "";
var thisTypeArgs = t.GetGenericArguments();
for( int i = 0; i < thisTypeArgs.Length && availableArguments.Count > 0; i++ ) {
if( i != 0 ) argString += ", ";
argString += GetCSharpRepresentation( availableArguments[0], trimArgCount );
availableArguments.RemoveAt( 0 );
}
// If there are type arguments, add them with < >
if( argString.Length > 0 ) {
value += "<" + argString + ">";
}
return value;
}
return t.Name;
}
对于这些类型(true为第2个参数):
typeof( List<string> ) )
typeof( List<Dictionary<int, string>> )
它返回:
List<String>
List<Dictionary<Int32, String>>
一般情况下,我敢打赌你可能不需要需要来获得代码的C#表示,如果你这样做,一些比C#语法更好的格式会更合适。
答案 1 :(得分:6)
你可以用这个:
public static string GetTypeName(Type t) {
if (!t.IsGenericType) return t.Name;
if (t.IsNested && t.DeclaringType.IsGenericType) throw new NotImplementedException();
string txt = t.Name.Substring(0, t.Name.IndexOf('`')) + "<";
int cnt = 0;
foreach (Type arg in t.GetGenericArguments()) {
if (cnt > 0) txt += ", ";
txt += GetTypeName(arg);
cnt++;
}
return txt + ">";
}
例如:
static void Main(string[] args) {
var obj = new Dictionary<string, Dictionary<HashSet<int>, int>>();
string s = GetTypeName(obj.GetType());
Console.WriteLine(s);
Console.ReadLine();
}
输出:
Dictionary<String, Dictionary<HashSet<Int32>, Int32>>
答案 2 :(得分:2)
typeof(List<string>).ToString()
答案 3 :(得分:2)
如果您有列表的实例,则可以调用.ToString()并获取以下内容
System.Collections.Generic.List`1[System.String]
这是除了直接针对类型而不是实例的其他答案提供的方法之外。
编辑:在您的编辑中,我认为如果不提供自己的解析方法就不可能,因为List<string>
是C#的简写,表示类型的实现方式,有点像if你写了typeof(int).ToString()
,捕获的不是“int”而是CTS名称, System.Int32 。
答案 4 :(得分:2)
这是我的实施,受益于@ Hans的上述回答和@ Jack在duplicate question上的回答。
public static string GetCSharpName( this Type type )
{
string result;
if ( primitiveTypes.TryGetValue( type, out result ) )
return result;
else
result = type.Name.Replace( '+', '.' );
if ( !type.IsGenericType )
return result;
else if ( type.IsNested && type.DeclaringType.IsGenericType )
throw new NotImplementedException();
result = result.Substring( 0, result.IndexOf( "`" ) );
return result + "<" + string.Join( ", ", type.GetGenericArguments().Select( GetCSharpName ) ) + ">";
}
static Dictionary<Type, string> primitiveTypes = new Dictionary<Type, string>
{
{ typeof(bool), "bool" },
{ typeof(byte), "byte" },
{ typeof(char), "char" },
{ typeof(decimal), "decimal" },
{ typeof(double), "double" },
{ typeof(float), "float" },
{ typeof(int), "int" },
{ typeof(long), "long" },
{ typeof(sbyte), "sbyte" },
{ typeof(short), "short" },
{ typeof(string), "string" },
{ typeof(uint), "uint" },
{ typeof(ulong), "ulong" },
{ typeof(ushort), "ushort" },
};
答案 5 :(得分:1)
Adam Sills's answer的改进,适用于非通用嵌套类型和泛型类型定义:
public class TypeNameStringExtensions
{
public static string GetCSharpRepresentation(Type t)
{
return GetCSharpRepresentation(t, new Queue<Type>(t.GetGenericArguments()));
}
static string GetCSharpRepresentation(Type t, Queue<Type> availableArguments)
{
string value = t.Name;
if (t.IsGenericParameter)
{
return value;
}
if (t.DeclaringType != null)
{
// This is a nested type, build the parent type first
value = GetCSharpRepresentation(t.DeclaringType, availableArguments) + "+" + value;
}
if (t.IsGenericType)
{
value = value.Split('`')[0];
// Build the type arguments (if any)
string argString = "";
var thisTypeArgs = t.GetGenericArguments();
for (int i = 0; i < thisTypeArgs.Length && availableArguments.Count > 0; i++)
{
if (i != 0) argString += ", ";
argString += GetCSharpRepresentation(availableArguments.Dequeue());
}
// If there are type arguments, add them with < >
if (argString.Length > 0)
{
value += "<" + argString + ">";
}
}
return value;
}
[TestCase(typeof(List<string>), "List<String>")]
[TestCase(typeof(List<Dictionary<int, string>>), "List<Dictionary<Int32, String>>")]
[TestCase(typeof(Stupid<int>.Stupider<int>), "Stupid<Int32>+Stupider<Int32>")]
[TestCase(typeof(Dictionary<int, string>.KeyCollection), "Dictionary<Int32, String>+KeyCollection")]
[TestCase(typeof(Nullable<Point>), "Nullable<Point>")]
[TestCase(typeof(Point?), "Nullable<Point>")]
[TestCase(typeof(TypeNameStringExtensions), "TypeNameStringExtensions")]
[TestCase(typeof(Another), "TypeNameStringExtensions+Another")]
[TestCase(typeof(G<>), "TypeNameStringExtensions+G<T>")]
[TestCase(typeof(G<string>), "TypeNameStringExtensions+G<String>")]
[TestCase(typeof(G<Another>), "TypeNameStringExtensions+G<TypeNameStringExtensions+Another>")]
[TestCase(typeof(H<,>), "TypeNameStringExtensions+H<T1, T2>")]
[TestCase(typeof(H<string, Another>), "TypeNameStringExtensions+H<String, TypeNameStringExtensions+Another>")]
[TestCase(typeof(Another.I<>), "TypeNameStringExtensions+Another+I<T3>")]
[TestCase(typeof(Another.I<int>), "TypeNameStringExtensions+Another+I<Int32>")]
[TestCase(typeof(G<>.Nested), "TypeNameStringExtensions+G<T>+Nested")]
[TestCase(typeof(G<string>.Nested), "TypeNameStringExtensions+G<String>+Nested")]
[TestCase(typeof(A<>.C<>), "TypeNameStringExtensions+A<B>+C<D>")]
[TestCase(typeof(A<int>.C<string>), "TypeNameStringExtensions+A<Int32>+C<String>")]
public void GetCSharpRepresentation_matches(Type type, string expected)
{
string actual = GetCSharpRepresentation(type);
Assert.AreEqual(expected, actual);
}
public class G<T>
{
public class Nested { }
}
public class A<B>
{
public class C<D> { }
}
public class H<T1, T2> { }
public class Another
{
public class I<T3> { }
}
}
public class Stupid<T1>
{
public class Stupider<T2>
{
}
}
我也选择放弃他的trimArgCount
,因为我看不清楚什么时候会有用,并且使用Queue<Type>
,因为那是意图(当它们存在时从前面拉出物品)。
答案 6 :(得分:1)
使用扩展程序获取好的类型名称的另一种方法:
typeof(Dictionary<string, Dictionary<decimal, List<int>>>).CSharpName();
// output is:
// Dictionary<String, Dictionary<Decimal, List<Int32>>>
扩展代码:
public static class TypeExtensions
{
public static string CSharpName(this Type type)
{
string typeName = type.Name;
if (type.IsGenericType)
{
var genArgs = type.GetGenericArguments();
if (genArgs.Count() > 0)
{
typeName = typeName.Substring(0, typeName.Length - 2);
string args = "";
foreach (var argType in genArgs)
{
string argName = argType.Name;
if (argType.IsGenericType)
argName = argType.CSharpName();
args += argName + ", ";
}
typeName = string.Format("{0}<{1}>", typeName, args.Substring(0, args.Length - 2));
}
}
return typeName;
}
}
答案 7 :(得分:1)
在某些情况下我遇到了其他答案的问题,即使用数组,所以我最后写了另一个。除了获取类型的简单名称之外,我不会使用Type.Name
或类似的文字,因为我不知道在不同的.Net版本中是否保证格式相同或与库的其他实现(我假设它不是)。
/// <summary>
/// For the given type, returns its representation in C# code.
/// </summary>
/// <param name="type">The type.</param>
/// <param name="genericArgs">Used internally, ignore.</param>
/// <param name="arrayBrackets">Used internally, ignore.</param>
/// <returns>The representation of the type in C# code.</returns>
public static string GetTypeCSharpRepresentation(Type type, Stack<Type> genericArgs = null, StringBuilder arrayBrackets = null)
{
StringBuilder code = new StringBuilder();
Type declaringType = type.DeclaringType;
bool arrayBracketsWasNull = arrayBrackets == null;
if (genericArgs == null)
genericArgs = new Stack<Type>(type.GetGenericArguments());
int currentTypeGenericArgsCount = genericArgs.Count;
if (declaringType != null)
currentTypeGenericArgsCount -= declaringType.GetGenericArguments().Length;
Type[] currentTypeGenericArgs = new Type[currentTypeGenericArgsCount];
for (int i = currentTypeGenericArgsCount - 1; i >= 0; i--)
currentTypeGenericArgs[i] = genericArgs.Pop();
if (declaringType != null)
code.Append(GetTypeCSharpRepresentation(declaringType, genericArgs)).Append('.');
if (type.IsArray)
{
if (arrayBrackets == null)
arrayBrackets = new StringBuilder();
arrayBrackets.Append('[');
arrayBrackets.Append(',', type.GetArrayRank() - 1);
arrayBrackets.Append(']');
Type elementType = type.GetElementType();
code.Insert(0, GetTypeCSharpRepresentation(elementType, arrayBrackets : arrayBrackets));
}
else
{
code.Append(new string(type.Name.TakeWhile(c => char.IsLetterOrDigit(c) || c == '_').ToArray()));
if (currentTypeGenericArgsCount > 0)
{
code.Append('<');
for (int i = 0; i < currentTypeGenericArgsCount; i++)
{
code.Append(GetTypeCSharpRepresentation(currentTypeGenericArgs[i]));
if (i < currentTypeGenericArgsCount - 1)
code.Append(',');
}
code.Append('>');
}
if (declaringType == null && !string.IsNullOrEmpty(type.Namespace))
{
code.Insert(0, '.').Insert(0, type.Namespace);
}
}
if (arrayBracketsWasNull && arrayBrackets != null)
code.Append(arrayBrackets.ToString());
return code.ToString();
}
我已经用这样的疯狂类型对它进行了测试,到目前为止它已经完美地运行了:
class C
{
public class D<D1, D2>
{
public class E
{
public class K<R1, R2, R3>
{
public class P<P1>
{
public struct Q
{
}
}
}
}
}
}
type = typeof(List<Dictionary<string[], C.D<byte, short[,]>.E.K<List<int>[,][], Action<List<long[][][,]>[], double[][,]>, float>.P<string>.Q>>[][,][,,,][][,,]);
// Returns "System.Collections.Generic.List<System.Collections.Generic.Dictionary<System.String[],Test.Program.C.D<System.Byte,System.Int16[,]>.E.K<System.Collections.Generic.List<System.Int32>[,][],System.Action<System.Collections.Generic.List<System.Int64[][][,]>[],System.Double[][,]>,System.Single>.P<System.String>.Q>>[][,][,,,][][,,]":
GetTypeCSharpRepresentation(type);
可能还有一些我没有想到的问题,但是有一个已知的问题:要检索名称,我只能获得满足条件char.IsLetterOrDigit(c) || c == '_'
的字符,直到找不到符号的字符{&# 39; t被找到,因此任何使用不符合条件的允许字符的类型名称都将失败。
答案 8 :(得分:0)
如果您想使用基本通用类型:
List<string> lstString = new List<string>();
Type type = lstString.GetType().GetGenericTypeDefinition();
假设你想要使用类型做某事,并且你真的不需要实际的字符串定义,这不是那么有用。
答案 9 :(得分:0)
对此表示欢迎,并认为我会分享自己的解决方案。它处理多个通用参数,可为空,锯齿状数组,多维数组,锯齿状/多维数组的组合以及上述任何项的任何嵌套组合。我主要将其用于日志记录,以便更轻松地识别复杂类型。
public static string GetGoodName(this Type type)
{
var sb = new StringBuilder();
void VisitType(Type inType)
{
if (inType.IsArray)
{
var rankDeclarations = new Queue<string>();
Type elType = inType;
do
{
rankDeclarations.Enqueue($"[{new string(',', elType.GetArrayRank() - 1)}]");
elType = elType.GetElementType();
} while (elType.IsArray);
VisitType(elType);
while (rankDeclarations.Count > 0)
{
sb.Append(rankDeclarations.Dequeue());
}
}
else
{
if (inType.IsGenericType)
{
var isNullable = inType.IsNullable();
var genargs = inType.GetGenericArguments().AsEnumerable();
var numer = genargs.GetEnumerator();
numer.MoveNext();
if (!isNullable) sb.Append($"{inType.Name.Substring(0, inType.Name.IndexOf('`'))}<");
VisitType(numer.Current);
while (numer.MoveNext())
{
sb.Append(",");
VisitType(numer.Current);
}
if (isNullable)
{
sb.Append("?");
}
else
{
sb.Append(">");
}
}
else
{
sb.Append(inType.Name);
}
}
}
VisitType(type);
var s = sb.ToString();
return s;
}
此:
typeof(Dictionary<int?, Tuple<string[], List<string[][,,,]>>>).GetGoodName()
...返回此:
Dictionary<Int32?,Tuple<String[],List<String[][,,,]>>>