這是我在寫程式碼產生器常遇到的小困擾。例如: 當透過Reflection偵測型別轉成屬性型別宣告時,typeof(T).ToString()會產生如"System.String"的完整型別名稱,故不時會出現如下的產出結果:

public class Player
{
    public System.String Name { get; set; }
    public System.Int32 Score { get; set; }
....略....

有沒有覺得程式碼裡充滿System.String、System.Int32 十分"矯情",一點都不像正常人會寫的程式(雖然它執行起來100%正確),充滿了賤人機械味。一時手癢想改善這點小瑕疵,便參考C#文件關於Predefined Types(預先定義的型別)的項目建成轉換字典,如此便可將產出程式碼的System.Int32換成親切的int,看起來賞心悅目多了!

        //REF: http://msdn.microsoft.com/en-us/library/aa664635(v=vs.71).aspx
        static string[] typeMap = new string[] {
            "object,System.Object",
            "string,System.String",
            "sbyte,System.SByte",
            "short,System.Int16",
            "int,System.Int32",
            "long,System.Int64",
            "byte,System.Byte",
            "ushort,System.UInt16",
            "uint,System.UInt32",
            "ulong,System.UInt64",
            "float,System.Single",
            "double,System.Double",
            "bool,System.Boolean",
            "char,System.Char",
            "decimal,System.Decimal"
        };
        static Dictionary<string, string> PT2Name =
            typeMap.ToDictionary(o => o.Split(',')[0], o => o.Split(',')[1]);
        static Dictionary<string, string> Name2PT =
            typeMap.ToDictionary(o => o.Split(',')[1], o => o.Split(',')[0]);
 
        static void Test()
        {
            string s = typeof(string).FullName;
            Console.WriteLine("{0}->{1}", s, Name2PT[s]);
            s = "uint";
            Console.WriteLine("{0}->{1}", s, PT2Name[s]);
                
        }

【同場加映】

透過Reflection取得泛型及巢狀類別相關型別時,會出現如"`"、"+"等符號。例如: typeof(Dictionary<string, Dictionary<DateTime, Boo.Foo>>).ToString(),會得到奇怪的字串結果: (Boo.Foo為定義在Boo裡的巢狀型別)

System.Collections.Generic.Dictionary`2[System.String,System.Collections.Generic.Dictionary`2[System.DateTime,Boo+Foo]]

如果直接在程式碼使用它當作類別宣告,包你錯到鬼哭神號。針對這個問題,我在Stackoverflow上找到一段高手寫的簡短函數,自己補上巢狀型別的"+"轉換,再接上前述的Predefined Type名稱置換、省略System.*命名空間(註: 需配合對應的using namespace宣告)等簡化,最後終於導回正軌,得到:

Dictionary<string,Dictionary<DateTime,Boo.Foo>>

範例程式碼如下,供有興趣的朋友參考:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
 
public class Boo
{
    public class Foo { }
}
 
class Program
{
    static void Main(string[] args)
    {
        Type t = typeof(Dictionary<string, Dictionary<DateTime, Boo.Foo>>);
        Console.WriteLine(t.ToString());
        Console.WriteLine(GetFriendlyTypeName(t));
        Console.Read();
    }
 
    //REF: http://msdn.microsoft.com/en-us/library/aa664635(v=vs.71).aspx
    static string[] typeMap = new string[] {
            "object,System.Object",
            "string,System.String",
            "sbyte,System.SByte",
            "short,System.Int16",
            "int,System.Int32",
            "long,System.Int64",
            "byte,System.Byte",
            "ushort,System.UInt16",
            "uint,System.UInt32",
            "ulong,System.UInt64",
            "float,System.Single",
            "double,System.Double",
            "bool,System.Boolean",
            "char,System.Char",
            "decimal,System.Decimal"
        };
    static Dictionary<string, string> PT2Name =
        typeMap.ToDictionary(o => o.Split(',')[0], o => o.Split(',')[1]);
    static Dictionary<string, string> Name2PT =
        typeMap.ToDictionary(o => o.Split(',')[1], o => o.Split(',')[0]);
 
 
 
    //REF: http://stackoverflow.com/questions/401681
    //Add nested type name, predefined types, System.* namespace trimming support
    public static string GetFriendlyTypeName(Type type)
    {
        Func<string, string> fixTypeName =
            (s) =>
            {
                s = s.Replace("+", "."); //Fix nested type name
                //Replace predefined types
                foreach (string n in Name2PT.Keys)
                    s = s.Replace(n, Name2PT[n]);
                //Trim System.* namespace
                s = Regex.Replace(s, @"System\.(\w+\.)*(?<n>\w+)", 
                    m => m.Groups["n"].Value);
                return s;
            };
 
        if (type.IsGenericParameter)
        {
            return fixTypeName(type.Name);
        }
 
        if (!type.IsGenericType)
        {
            return fixTypeName(type.FullName);
        }
 
        var builder = new System.Text.StringBuilder();
        var name = type.Name;
        var index = name.IndexOf("`");
        builder.AppendFormat(
            fixTypeName(type.Namespace + "." + name.Substring(0, index)));
        builder.Append('<');
        var first = true;
        foreach (var arg in type.GetGenericArguments())
        {
            if (!first)
            {
                builder.Append(',');
            }
            builder.Append(GetFriendlyTypeName(arg));
            first = false;
        }
        builder.Append('>');
        return builder.ToString();
    }
}

Comments

# by jurio

原來有GetGenericArguments()… 前陣子這題我只用字串處理寫了好久><

Post a comment