CODE-自動產生程式碼時將System.Int32轉為int
1 |
這是我在寫程式碼產生器常遇到的小困擾。例如: 當透過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()… 前陣子這題我只用字串處理寫了好久><