给定泛型类型,包括
List<string>
Nullable<Int32>
如何获得C#的通用名称?
var t = typeof(Nullable<DateTime>);
var s = t.GetGenericTypeDefinition().Name + "<" + t.GetGenericArguments()[0].Name + ">";
这会产生
"Nullable`1<DateTime>"
,但我需要
"Nullable<DateTime>"
答案 0 :(得分:58)
我看到你已经接受了答案,但老实说,如果你把那里的东西和你已经写好的东西结合起来,那么答案就不足以做到这一点。它是在正确的轨道上,但您的代码仅适用于具有一个通用参数的泛型类型,并且只有当泛型类型参数本身不是通用的时才会起作用!
这是一个功能(作为扩展方法编写),实际上应该适用于所有情况:
public static class TypeExtensions
{
public static string ToGenericTypeString(this Type t)
{
if (!t.IsGenericType)
return t.Name;
string genericTypeName = t.GetGenericTypeDefinition().Name;
genericTypeName = genericTypeName.Substring(0,
genericTypeName.IndexOf('`'));
string genericArgs = string.Join(",",
t.GetGenericArguments()
.Select(ta => ToGenericTypeString(ta)).ToArray());
return genericTypeName + "<" + genericArgs + ">";
}
}
此功能是递归且安全的。如果你在这个输入上运行它:
Console.WriteLine(
typeof(Dictionary<string, List<Func<string, bool>>>)
.ToGenericTypeString());
你得到这个(正确的)输出:
Dictionary<String,List<Func<String,Boolean>>>
答案 1 :(得分:6)
虽然接受的解决方案仅适用于名称或非嵌套全名(通过将名称替换为@Ose E&#39的答案中的全名),但是对于嵌套类型,它仍然不起作用,并且也不适用于泛型类型的数组。
所以这是一个可行的解决方案,(但请注意,此解决方案仅设置实际参数,仅在设置了所有参数时,换句话说,即使声明类型提供了类型arguemts,只要最里面的泛型类型没有,它甚至不会出现在基础上。)
public static string ToGenericTypeString(this Type t, params Type[] arg)
{
if (t.IsGenericParameter || t.FullName == null) return t.Name;//Generic argument stub
bool isGeneric = t.IsGenericType || t.FullName.IndexOf('`') >= 0;//an array of generic types is not considered a generic type although it still have the genetic notation
bool isArray = !t.IsGenericType && t.FullName.IndexOf('`') >= 0;
Type genericType = t;
while (genericType.IsNested && genericType.DeclaringType.GetGenericArguments().Count()==t.GetGenericArguments().Count())//Non generic class in a generic class is also considered in Type as being generic
{
genericType = genericType.DeclaringType;
}
if (!isGeneric) return t.FullName.Replace('+', '.');
var arguments = arg.Any() ? arg : t.GetGenericArguments();//if arg has any then we are in the recursive part, note that we always must take arguments from t, since only t (the last one) will actually have the constructed type arguments and all others will just contain the generic parameters
string genericTypeName = genericType.FullName;
if (genericType.IsNested)
{
var argumentsToPass = arguments.Take(genericType.DeclaringType.GetGenericArguments().Count()).ToArray();//Only the innermost will return the actual object and only from the GetGenericArguments directly on the type, not on the on genericDfintion, and only when all parameters including of the innermost are set
arguments = arguments.Skip(argumentsToPass.Count()).ToArray();
genericTypeName = genericType.DeclaringType.ToGenericTypeString(argumentsToPass) + "." + genericType.Name;//Recursive
}
if (isArray)
{
genericTypeName = t.GetElementType().ToGenericTypeString() + "[]";//this should work even for multidimensional arrays
}
if (genericTypeName.IndexOf('`') >= 0)
{
genericTypeName = genericTypeName.Substring(0, genericTypeName.IndexOf('`'));
string genericArgs = string.Join(",", arguments.Select(a => a.ToGenericTypeString()).ToArray());
//Recursive
genericTypeName = genericTypeName + "<" + genericArgs + ">";
if (isArray) genericTypeName += "[]";
}
if (t != genericType)
{
genericTypeName += t.FullName.Replace(genericType.FullName, "").Replace('+','.');
}
if (genericTypeName.IndexOf("[") >= 0 && genericTypeName.IndexOf("]") != genericTypeName.IndexOf("[") +1) genericTypeName = genericTypeName.Substring(0, genericTypeName.IndexOf("["));//For a non generic class nested in a generic class we will still have the type parameters at the end
return genericTypeName;
}
答案 2 :(得分:0)
这是我的解决方案,它也适用于嵌套类和泛型:
public static string GenericTypeString(this Type t)
{
if (!t.IsGenericType)
{
return t.GetFullNameWithoutNamespace()
.ReplacePlusWithDotInNestedTypeName();
}
return t.GetGenericTypeDefinition()
.GetFullNameWithoutNamespace()
.ReplacePlusWithDotInNestedTypeName()
.ReplaceGenericParametersInGenericTypeName(t);
}
private static string GetFullNameWithoutNamespace(this Type type)
{
if (type.IsGenericParameter)
{
return type.Name;
}
const int dotLength = 1;
return type.FullName.Substring(type.Namespace.Length + dotLength);
}
private static string ReplacePlusWithDotInNestedTypeName(this string typeName)
{
return typeName.Replace('+', '.');
}
private static string ReplaceGenericParametersInGenericTypeName(this string typeName, Type t)
{
var genericArguments = t.GetGenericArguments();
const string regexForGenericArguments = @"`[1-9]\d*";
var rgx = new Regex(regexForGenericArguments);
typeName = rgx.Replace(typeName, match =>
{
var currentGenericArgumentNumbers = int.Parse(match.Value.Substring(1));
var currentArguments = string.Join(",", genericArguments.Take(currentGenericArgumentNumbers).Select(GenericTypeString));
genericArguments = genericArguments.Skip(currentGenericArgumentNumbers).ToArray();
return string.Concat("<", currentArguments, ">");
});
return typeName;
}
答案 3 :(得分:0)
这将产生与cs代码生成器完全相同的代码结果。 我改进了yoel halb的代码。
/// <summary>
/// Gets the CS Type Code for a type
/// </summary>
/// <param name="type">The type.</param>
/// <returns></returns>
/// <exception cref="System.ArgumentNullException">type</exception>
public static string GetCSTypeName(this Type type)
{
if (type == typeof(string))
{
return "string";
}
else if (type == typeof(object)) { return "object"; }
else if (type == typeof(bool)) { return "bool"; }
else if (type == typeof(char)) { return "char"; }
else if (type == typeof(int)) { return "int"; }
else if (type == typeof(float)) { return "float"; }
else if (type == typeof(double)) { return "double"; }
else if (type == typeof(long)) { return "long"; }
else if (type == typeof(ulong)) { return "ulong"; }
else if (type == typeof(uint)) { return "uint"; }
else if (type == typeof(byte)) { return "byte"; }
else if (type == typeof(Int64)) { return "Int64"; }
else if (type == typeof(short)) { return "short"; }
else if (type == typeof(decimal)) { return "decimal"; }
else if (type.IsGenericType)
{
return $"{ToGenericTypeString(type)}";
}
else if (type.IsArray)
{
List<string> arrayLength = new List<string>();
for (int i = 0; i < type.GetArrayRank(); i++)
{
arrayLength.Add("[]");
}
return GetCSTypeName(type.GetElementType()) + string.Join("", arrayLength).Replace("+", ".");
}
else
{
return type.FullName.Replace("+", ".");
}
}
private static string ToCSReservatedWord(this Type type, bool fullName)
{
if (type == typeof(string))
{
return "string";
}
else if (type == typeof(object)) { return "object"; }
else if (type == typeof(bool)) { return "bool"; }
else if (type == typeof(char)) { return "char"; }
else if (type == typeof(int)) { return "int"; }
else if (type == typeof(float)) { return "float"; }
else if (type == typeof(double)) { return "double"; }
else if (type == typeof(long)) { return "long"; }
else if (type == typeof(ulong)) { return "ulong"; }
else if (type == typeof(uint)) { return "uint"; }
else if (type == typeof(byte)) { return "byte"; }
else if (type == typeof(Int64)) { return "Int64"; }
else if (type == typeof(short)) { return "short"; }
else if (type == typeof(decimal)) { return "decimal"; }
else
{
if (fullName)
{
return type.FullName;
}
else
{
return type.Name;
}
}
}
public static string ToGenericTypeString(this Type t, params Type[] arg)
{
if (t.IsGenericParameter || t.FullName == null) return t.FullName;//Generic argument stub
bool isGeneric = t.IsGenericType || t.FullName.IndexOf('`') >= 0;//an array of generic types is not considered a generic type although it still have the genetic notation
bool isArray = !t.IsGenericType && t.FullName.IndexOf('`') >= 0;
Type genericType = t;
while (genericType.IsNested && genericType.DeclaringType.GetGenericArguments().Count() == t.GetGenericArguments().Count())//Non generic class in a generic class is also considered in Type as being generic
{
genericType = genericType.DeclaringType;
}
if (!isGeneric) return ToCSReservatedWord(t, true).Replace('+', '.');
var arguments = arg.Any() ? arg : t.GetGenericArguments();//if arg has any then we are in the recursive part, note that we always must take arguments from t, since only t (the last one) will actually have the constructed type arguments and all others will just contain the generic parameters
string genericTypeName = genericType.ToCSReservatedWord(true);
if (genericType.IsNested)
{
var argumentsToPass = arguments.Take(genericType.DeclaringType.GetGenericArguments().Count()).ToArray();//Only the innermost will return the actual object and only from the GetGenericArguments directly on the type, not on the on genericDfintion, and only when all parameters including of the innermost are set
arguments = arguments.Skip(argumentsToPass.Count()).ToArray();
genericTypeName = genericType.DeclaringType.ToGenericTypeString(argumentsToPass) + "." + ToCSReservatedWord(genericType, false);//Recursive
}
if (isArray)
{
genericTypeName = t.GetElementType().ToGenericTypeString() + "[]";//this should work even for multidimensional arrays
}
if (genericTypeName.IndexOf('`') >= 0)
{
genericTypeName = genericTypeName.Substring(0, genericTypeName.IndexOf('`'));
string genericArgs = string.Join(", ", arguments.Select(a => a.ToGenericTypeString()).ToArray());
//Recursive
genericTypeName = genericTypeName + "<" + genericArgs + ">";
if (isArray) genericTypeName += "[]";
}
if (t != genericType)
{
genericTypeName += t.FullName.Replace(genericType.ToCSReservatedWord(true), "").Replace('+', '.');
}
if (genericTypeName.IndexOf("[") >= 0 && genericTypeName.IndexOf("]") != genericTypeName.IndexOf("[") + 1) genericTypeName = genericTypeName.Substring(0, genericTypeName.IndexOf("["));//For a non generic class nested in a generic class we will still have the type parameters at the end
return genericTypeName;
}
这将按预期通过以下单元测试。
[TestClass]
public class GetCSName
{
private string GetCSCompilerName(Type type)
{
if (type == null)
{
throw new ArgumentNullException(nameof(type));
}
var compiler = new CSharpCodeProvider();
var typeRef = new CodeTypeReference(type);
return compiler.GetTypeOutput(typeRef);
}
[TestMethod]
public void TestMethod1()
{
List<Type> typesToTest = new List<Type>();
typesToTest.Add(typeof(string));
typesToTest.Add(typeof(string[]));
typesToTest.Add(typeof(object[]));
typesToTest.Add(typeof(bool[]));
typesToTest.Add(typeof(string));
typesToTest.Add(typeof(object));
typesToTest.Add(typeof(int));
typesToTest.Add(typeof(double));
typesToTest.Add(typeof(float));
typesToTest.Add(typeof(bool));
typesToTest.Add(typeof(char));
typesToTest.Add(typeof(decimal));
typesToTest.Add(typeof(decimal?[]));
typesToTest.Add(typeof(decimal?[][]));
typesToTest.Add(typeof(Int64));
typesToTest.Add(typeof(Guid));
typesToTest.Add(typeof(int?));
typesToTest.Add(typeof(double?));
typesToTest.Add(typeof(float?));
typesToTest.Add(typeof(bool?));
typesToTest.Add(typeof(char?));
typesToTest.Add(typeof(decimal?));
typesToTest.Add(typeof(Int64?));
typesToTest.Add(typeof(Guid?));
typesToTest.Add(typeof(List<string>));
typesToTest.Add(typeof(Dictionary<string, Guid>));
typesToTest.Add(typeof(Dictionary<string, Guid>[]));
typesToTest.Add(typeof(Dictionary<string, Guid?>));
typesToTest.Add(typeof(Dictionary<string, Dictionary<string, Guid?>>));
typesToTest.Add(typeof(Dictionary<string, Dictionary<string, Guid?>>[]));
typesToTest.Add(typeof(Dictionary<string, Dictionary<string, Guid?>>[][]));
typesToTest.Add(typeof(int[]));
typesToTest.Add(typeof(int[][]));
typesToTest.Add(typeof(int[][][]));
typesToTest.Add(typeof(int[][][][]));
typesToTest.Add(typeof(int[][][][][]));
typesToTest.Add(typeof(TestClass));
typesToTest.Add(typeof(List<TestClass>));
typesToTest.Add(typeof(Dictionary<TestClass, TestClass>));
typesToTest.Add(typeof(Dictionary<string, TestClass>));
typesToTest.Add(typeof(List<Dictionary<string, TestClass>>));
typesToTest.Add(typeof(List<Dictionary<string, GenericTestClass<string>>>));
typesToTest.Add(typeof(GenericTestClass<string>.SecondSubType<decimal>));
typesToTest.Add(typeof(GenericTestClass<string>.SecondSubType));
typesToTest.Add(typeof(GenericTestClass<string, int>.SecondSubType));
typesToTest.Add(typeof(GenericTestClass<string, Dictionary<string,int>>.SecondSubType<string>));
typesToTest.Add(typeof(GenericTestClass<string, Dictionary<string, int>>.SecondSubType<GenericTestClass<string, Dictionary<string, int>>>));
foreach (var t in typesToTest)
{
if (GetCSCompilerName(t) != t.GetCSTypeName())
{
Console.WriteLine($"FullName:\r\n{t.FullName}");
Console.WriteLine("C " + GetCSCompilerName(t));
Console.WriteLine("R " + t.GetCSTypeName());
Console.WriteLine("Equal: " + (GetCSCompilerName(t) == t.GetCSTypeName()));
Console.WriteLine();
Assert.Fail($"From CSharpCodeProvider '{GetCSCompilerName(t)}' is not equal to {t.GetCSTypeName()}");
}
else
{
Console.WriteLine($"Passed: {t.GetCSTypeName()}");
//ignore because of equal.
}
}
}
public class TestClass
{
}
public class GenericTestClass<T>
{
public class SecondSubType
{
}
public class SecondSubType<T2>
{
}
}
public class GenericTestClass<T1,T2>
{
public class SecondSubType
{
}
public class SecondSubType<T2>
{
}
}
}
结果将是:
Passed: string
Passed: string[]
Passed: object[]
Passed: bool[]
Passed: string
Passed: object
Passed: int
Passed: double
Passed: float
Passed: bool
Passed: char
Passed: decimal
Passed: System.Nullable<decimal>[]
Passed: System.Nullable<decimal>[][]
Passed: long
Passed: System.Guid
Passed: System.Nullable<int>
Passed: System.Nullable<double>
Passed: System.Nullable<float>
Passed: System.Nullable<bool>
Passed: System.Nullable<char>
Passed: System.Nullable<decimal>
Passed: System.Nullable<long>
Passed: System.Nullable<System.Guid>
Passed: System.Collections.Generic.List<string>
Passed: System.Collections.Generic.Dictionary<string, System.Guid>
Passed: System.Collections.Generic.Dictionary<string, System.Guid>[]
Passed: System.Collections.Generic.Dictionary<string, System.Nullable<System.Guid>>
Passed: System.Collections.Generic.Dictionary<string, System.Collections.Generic.Dictionary<string, System.Nullable<System.Guid>>>
Passed: System.Collections.Generic.Dictionary<string, System.Collections.Generic.Dictionary<string, System.Nullable<System.Guid>>>[]
Passed: System.Collections.Generic.Dictionary<string, System.Collections.Generic.Dictionary<string, System.Nullable<System.Guid>>>[][]
Passed: int[]
Passed: int[][]
Passed: int[][][]
Passed: int[][][][]
Passed: int[][][][][]
Passed: Eneon.Common.Utils.Extensions.Tests.GetCSName.TestClass
Passed: System.Collections.Generic.List<Eneon.Common.Utils.Extensions.Tests.GetCSName.TestClass>
Passed: System.Collections.Generic.Dictionary<Eneon.Common.Utils.Extensions.Tests.GetCSName.TestClass, Eneon.Common.Utils.Extensions.Tests.GetCSName.TestClass>
Passed: System.Collections.Generic.Dictionary<string, Eneon.Common.Utils.Extensions.Tests.GetCSName.TestClass>
Passed: System.Collections.Generic.List<System.Collections.Generic.Dictionary<string, Eneon.Common.Utils.Extensions.Tests.GetCSName.TestClass>>
Passed: System.Collections.Generic.List<System.Collections.Generic.Dictionary<string, Eneon.Common.Utils.Extensions.Tests.GetCSName.GenericTestClass<string>>>
Passed: Eneon.Common.Utils.Extensions.Tests.GetCSName.GenericTestClass<string>.SecondSubType<decimal>
Passed: Eneon.Common.Utils.Extensions.Tests.GetCSName.GenericTestClass<string>.SecondSubType
Passed: Eneon.Common.Utils.Extensions.Tests.GetCSName.GenericTestClass<string, int>.SecondSubType
Passed: Eneon.Common.Utils.Extensions.Tests.GetCSName.GenericTestClass<string, System.Collections.Generic.Dictionary<string, int>>.SecondSubType<string>
Passed: Eneon.Common.Utils.Extensions.Tests.GetCSName.GenericTestClass<string, System.Collections.Generic.Dictionary<string, int>>.SecondSubType<Eneon.Common.Utils.Extensions.Tests.GetCSName.GenericTestClass<string, System.Collections.Generic.Dictionary<string, int>>>
答案 4 :(得分:-1)
@Aaronaught的次要补充
public string ToGenericTypeString(Type t)
{
if (!t.IsGenericType)
return t.FullName;
string genericTypeName = t.GetGenericTypeDefinition().FullName;
genericTypeName = genericTypeName.Substring(0,
genericTypeName.IndexOf('`'));
string genericArgs = string.Join(",",
t.GetGenericArguments()
.Select(ta => ToGenericTypeString(ta)).ToArray());
return genericTypeName + "<" + genericArgs + ">";
}