這將導致酷似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>>>
很遺憾的CLR不來使用此功能。 – 2010-03-15 17:53:49
@Paul Ruane:我同意在某些情況下它可能是有用的,但是,CLR是語言不可知的,並且沒有辦法實現這樣的事情,以便在C#,VB.NET, F#,IronPython和所有其他CLR語言。帶反引號的奇怪名字實際上是真正的CLR類型名稱;上面的格式是特定於C#的。 – Aaronaught 2010-03-15 19:00:26
啊,是的,好點。我有隧道C#願景! – 2010-03-15 20:21:26