C# 小技巧 - 使用匿名型別傳遞多參數
| | 3 | | 5,417 |
專案有個 HTTP POST 呼叫特定 URL 的共用函式需求,參數採完全彈性,給什麼就組什麼,直接轉成 p1=...&p2=...&p3=... application/x-www-form-urlencode 格式送出。
若用 .NET 寫,最省事的做法是將參數轉成 NameValueCollection 傳給 WebClient.UploadValues(),全靠現成元件,輕鬆秒殺。(延伸閱讀:利用 WebClient 類別模擬 HTTP POST 表單送出的注意事項 by 保哥)
對呼叫端而言,用 Dictionary<string, string> 比 NameValueCollection 順手,因此我的第一個版本長這樣:
static string url = "http://localhost/aspnet/post/";
static void ClassicPostRequest(Dictionary<string, string> data)
{
var wc = new WebClient();
var nvc = new NameValueCollection();
foreach (var kv in data)
nvc[kv.Key] = kv.Value;
var resp = wc.UploadValues(url, nvc);
Console.WriteLine(Encoding.UTF8.GetString(resp));
}
static void Main(string[] args)
{
var guid = Guid.NewGuid();
var time = DateTime.Now.ToString("yyyy-MM-dd");
ClassicPostRequest(new Dictionary<string, string>
{
["num"] = "123",
["letters"] = "ABC",
["time"] = time,
["guid"] = guid.ToString()
});
Console.Read();
}
如果別太挑剔,這個版本已經蠻好用。
BUT! Dapper 寫多用慣 new { prop1, prop2, prop3 = ... } 傳參數的簡潔寫法,new Dictionary<string, string> { ["prop1"] = prop1Value ... } 怎麼看怎麼 Low,大概就是所謂「曾經滄海難為水」吧。
cn.Query<ResultItem>("SELECT * FROM Items WHERE Col1 = @p1 AND Col2 = @p2 AND Col3 IN @array",
new { p1, p2, array = new string[] { "A", "B" } });
何不仿效 Dapper,也用自訂匿名物件彈性指定參數呢?試寫了一下,System.Reflection 加 LINQ 三兩下搞定:
static string url = "http://localhost/aspnet/post/";
static void NewPostRequest(object args) {
var wc = new WebClient();
var resp = wc.UploadValues(url, AnonymousTypeToNameValueCollection(args));
Console.WriteLine(Encoding.UTF8.GetString(resp));
}
static NameValueCollection AnonymousTypeToNameValueCollection(object args)
{
var postData = new NameValueCollection();
args.GetType().GetProperties()
.ToList()
.ForEach(p =>
{
var valObj = p.GetValue(args);
//TODO 視需要自訂物件轉字串邏輯
var valStr = p.PropertyType == typeof(DateTime) ?
((DateTime)valObj).ToString("yyyy-MM-dd HH:mm:ss") :
valObj.ToString();
postData.Add(p.Name, valStr);
});
return postData;
}
static void Main(string[] args)
{
var guid = Guid.NewGuid();
var time = DateTime.Now.ToString("yyyy-MM-dd");
NewPostRequest(new
{
num = "123",
letters = "ABC",
time,
guid
});
Console.Read();
}
是不是優雅多了?
Example of passing parameters as anonymous object to function in C#
Comments
# by Ammon
有個 RouteValueDictionary 可以參考看看 https://docs.microsoft.com/zh-tw/dotnet/api/system.web.routing.routevaluedictionary.-ctor?view=netframework-4.8#System_Web_Routing_RouteValueDictionary__ctor_System_Object_
# by 凱大
static void NewPostRequest<T>(T args, Func<T, NameValueCollection> func) { var wc = new WebClient(); var resp = wc.UploadValues(url, func(args)); Console.WriteLine(Encoding.UTF8.GetString(resp)); } NewPostRequest(new { num = "123", letters = "ABC", time, guid }, obj => /* 在這裡 obj 的型別為前面的匿名型別 可以不用 reflection 就存取 property */ );
# by ByTIM
用 dynamic ,不知道行不行?