我们最后再来看一下更复杂的应用,Peoplee有Friends属性,这是一个集合属性,我们想获取朋友的个数,并列出朋友的名字,如下:
People p5 = new People { Id = 1, Name = "鹤冲天"};
p5.AddFriend(new People { Id = 11, Name = "南霸天" });
p5.AddFriend(new People { Id = 12, Name = "日中天" });
string s5 = p5.ToString5("[Name] 目前有 [Friends: .Count] 个朋友:[Friends: .Name]。");
p5.AddFriend(new People { Id = 11, Name = "南霸天" });
p5.AddFriend(new People { Id = 12, Name = "日中天" });
string s5 = p5.ToString5("[Name] 目前有 [Friends: .Count] 个朋友:[Friends: .Name]。");
注意,行4中的Count及Name前都加了个小点,表示是将集合进行操作,这个小点是我看着方便自己定义的。再来看实现代码,到版本五了:
public static string ToString5(this object obj, string format)
{
MatchEvaluator evaluator = match =>
{
string[] propertyNames = match.Groups["Name"].Value.Split('.');
string propertyFormat = match.Groups["Format"].Value;
object propertyValue = obj;
try
{
foreach (string propertyName in propertyNames)
propertyValue = propertyValue.GetPropertyValue(propertyName);
}
catch
{
return match.Value;
}
if (string.IsNullOrEmpty(propertyFormat) == false)
{
if (propertyFormat.StartsWith("."))
{
string subPropertyName = propertyFormat.Substring(1);
IEnumerable<object> objs = ((IEnumerable)propertyValue).Cast<object>();
if (subPropertyName == "Count")
return objs.Count().ToString();
else
{
string[] subProperties = objs.Select(
o => o.GetPropertyValue(subPropertyName).ToString()).ToArray();
return string.Join(", ", subProperties);
}
}
else
return string.Format("{0:" + propertyFormat + "}", propertyValue);
}
else return propertyValue.ToString();
};
string pattern = @"\[(?<Name>[^\[\]:]+)(\s*[:]\s*(?<Format>[^\[\]:]+))?\]";
return Regex.Replace(format, pattern, evaluator, RegexOptions.Compiled);
}
{
MatchEvaluator evaluator = match =>
{
string[] propertyNames = match.Groups["Name"].Value.Split('.');
string propertyFormat = match.Groups["Format"].Value;
object propertyValue = obj;
try
{
foreach (string propertyName in propertyNames)
propertyValue = propertyValue.GetPropertyValue(propertyName);
}
catch
{
return match.Value;
}
if (string.IsNullOrEmpty(propertyFormat) == false)
{
if (propertyFormat.StartsWith("."))
{
string subPropertyName = propertyFormat.Substring(1);
IEnumerable<object> objs = ((IEnumerable)propertyValue).Cast<object>();
if (subPropertyName == "Count")
return objs.Count().ToString();
else
{
string[] subProperties = objs.Select(
o => o.GetPropertyValue(subPropertyName).ToString()).ToArray();
return string.Join(", ", subProperties);
}
}
else
return string.Format("{0:" + propertyFormat + "}", propertyValue);
}
else return propertyValue.ToString();
};
string pattern = @"\[(?<Name>[^\[\]:]+)(\s*[:]\s*(?<Format>[^\[\]:]+))?\]";
return Regex.Replace(format, pattern, evaluator, RegexOptions.Compiled);
}
执行结果:
比较不可思议吧,下面简单分析一下。行22~行33是对集合进行操作的相关处理,这里只是简单实现了Count,当然也可以实现Min、Max、Sum、Average等等。“.Name”这个表示方法不太好,这里主要是为了展示,大家能明白了就好。
ToString(string format)扩展带来便利性的同时,也会带来相应的性能损失,两者很难兼得。
最后重申下,本系列文章,侧重想法,所给的代码仅供演示、参考,没有考虑性能、异常处理等,如需实际使用,请自行完善。