為了給 List<T> 加上支援並列排名的 Rank 擴充方法,首先得解決「如何為任意型別 T 動態新增屬性」的問題。本文介紹使用 System.CodeDom.Compiler 動態編譯型別、以及透過反射動態存取屬性值的完整實作。
利用 ASCII 碼隨機組合英文字母:
public static string CreateEnglish(int number)
{
Random random = new Random(DateTime.Now.Millisecond);
List<string> en = new List<string>();
Enumerable.Range(97, 26).ToList<int>().ForEach(t => en.Add(((char)t).ToString()));
Enumerable.Range(65, 26).ToList<int>().ForEach(t => en.Add(((char)t).ToString()));
string reuslt = string.Empty;
for (int i = 0; i < number; i++)
reuslt += en[random.Next(en.Count)];
return reuslt;
}
以 Dictionary<string, string> 記錄屬性名稱與完整型別名稱,組合原始碼字串,呼叫 CSharpCodeProvider 編譯:
private static Type DynamicCreateType(Dictionary<string, string> dictionary, string[] referencedAssemblies)
{
CSharpCodeProvider p = new CSharpCodeProvider(new Dictionary<string, string>() { { "CompilerVersion", "v3.5" } });
CompilerParameters param = new CompilerParameters(referencedAssemblies);
string className = "__" + CreateEnglish(10);
StringBuilder classCode = new StringBuilder();
classCode.Append("public class ");
classCode.Append(className);
classCode.Append("{");
foreach (var item in dictionary)
classCode.Append("public " + item.Value + " " + item.Key + "{ get;set;}");
classCode.Append("}");
CompilerResults rel = p.CompileAssemblyFromSource(param, classCode.ToString());
return rel.CompiledAssembly.GetType(className);
}
取得物件所有公開屬性名稱與值:
public static Dictionary<string, object> DynamicGetProperty(object obj)
{
Dictionary<string, object> temp = new Dictionary<string, object>();
Array.ForEach<PropertyInfo>(obj.GetType().GetProperties(),
p => temp.Add(p.Name, p.GetValue(obj, null)));
return temp;
}
依字典為動態型別實例賦值:
private static object DynamicSetProperty(Type type, Dictionary<string, object> value)
{
object o = type.GetConstructor(Type.EmptyTypes).Invoke(null);
foreach (var item in value)
o.GetType().GetProperty(item.Key).SetValue(o, item.Value, null);
return o;
}
取得型別所有公開屬性與型別名稱:
private static Dictionary<string, string> TypeTransform(Type type)
{
Dictionary<string, string> dictionary = new Dictionary<string, string>();
Array.ForEach<PropertyInfo>(type.GetProperties(),
p => dictionary.Add(p.Name, p.PropertyType.FullName));
return dictionary;
}
有了這些基礎建設,下篇即可實作 Rank 方法,完成並列排名功能。