2010-03-15 50 views

回答

53

我看到你已經接受了一個答案,但說實話,如果你只是把你已經寫的內容和那些內容結合起來,那麼這個答案不足以做到這一點。它在正確的軌道上,但是你的代碼只適用於只有一個泛型參數的泛型類型,並且它只有在泛型類型參數本身不是泛型時才能工作!

這是一個函數(寫爲擴展方法),應在所有情況下的實際工作:

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

很遺憾的CLR不來使用此功能。 – 2010-03-15 17:53:49

+3

@Paul Ruane:我同意在某些情況下它可能是有用的,但是,CLR是語言不可知的,並且沒有辦法實現這樣的事情,以便在C#,VB.NET, F#,IronPython和所有其他CLR語言。帶反引號的奇怪名字實際上是真正的CLR類型名稱;上面的格式是特定於C#的。 – Aaronaught 2010-03-15 19:00:26

+0

啊,是的,好點。我有隧道C#願景! – 2010-03-15 20:21:26

0

小調除了@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 + ">"; 
} 
+0

看起來您唯一改變的地方是從'Name'到' FullName'?我不確定這是否有足夠的實質性變化來證明另一個答案的正確性 - 它可能應該作爲評論發佈。 – Aaronaught 2014-02-26 16:32:35

+0

這不是一個替代方案。全名有助於解決名稱空間問題。 – 2014-02-26 19:18:40

5

雖然接受的解決方案有利於剛名稱或非嵌套全名(通過在@Ose E的答案中將名稱替換爲全名),但是對於嵌套類型,它仍然不起作用,也不適用於泛型類型的數組。

所以這裏有一個解決方案可以工作,(但是請注意,這個解決方案只會設置實際的參數,只有當所有參數都被設置,換句話說,即使聲明類型提供了類型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; 
    } 
+0

在我正在進行的項目中使用此解決方案,效果很好 – Oren 2014-09-23 10:02:16

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; 
    } 
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>>> 
相關問題