文字转换所有对象的属性,有没有更好的方法?
问题描述:
目前,我这样做:文字转换所有对象的属性,有没有更好的方法?
我的文字,看起来像:
Hello ${user.name}, this is ....
而且我这样做:
public string TransformUser(User user, string text)
{
StringBuilder sb = new StringBuilder(text);
sb.Replace("${user.name}", user.Name);
...
...
return sb.ToString();
}
有没有更好的办法,也许使用反射莫名其妙地循环通过类公共属性?
编辑
是否有可能使这种方法一般,所以我可以传递任何对象呢?
答
通过所有属性循环使用反射和替换字符串中的按键看起来大致是这样的:
var args = new object[0];
foreach(var prop in typeof(User).GetProperties()) {
if (prop.CanRead) {
string val = prop.GetGetMethod().Invoke(user, args).ToString();
sb.Replace("${user." + prop.Name +"}", val);
}
}
它使用CanRead
检查,如果酒店有吸气,然后调用吸气读值。只需使用ToString
将值转换为字符串,这可能适用于基元类型(取决于所需的行为)。这是区分大小写的,所以如果用户使用小写字母(如你的例子)写密钥,你可能想要使用ToLower
。
答
您可以通过调用typeof(User).GetProperties()
来遍历属性。
答
我写了一个StringTemplate
类,可能会根据您的需要进行修改...它的行为与String.Format
类似,主要区别在于:您可以使用名称作为占位符,而不是索引。要格式化的值可以指定为IDictionary<string, object>
或任何对象(在这种情况下,每个占位符将被替换为具有相同名称的属性的值)。
例如:
// with a dictionary :
var values = new Dictionary<string, object>
{
{ "Title", "Mr." },
{ "LastName", "Smith" }
};
string a = StringTemplate.Format("Hello {Title} {LastName}", values);
// with an anonymous type :
string b = StringTemplate.Format(
"Hello {Title} {LastName}",
new { Title = "Mr.", LastName = "Smith" });
如果需要使用相同的模板多次,您可以创建StringTemplate
一个实例,并重新使用它获得更好的性能(模板字符串将只解析一次)。
您也可以指定格式修饰符,如String.Format
。
满足您的实际需求,这个类将需要一些调整,但它不应该太难......
下面的代码:
public class StringTemplate
{
private string _template;
private static Regex _regex = new Regex(@"(?<open>{+)(?<key>\w+)(?<format>:[^}]+)?(?<close>}+)", RegexOptions.Compiled);
public StringTemplate(string template)
{
template.CheckArgumentNull("template");
this._template = template;
ParseTemplate();
}
private string _templateWithIndexes;
private List<string> _placeholders;
private void ParseTemplate()
{
_placeholders = new List<string>();
MatchEvaluator evaluator = (m) =>
{
if (m.Success)
{
string open = m.Groups["open"].Value;
string close = m.Groups["close"].Value;
string key = m.Groups["key"].Value;
string format = m.Groups["format"].Value;
if (open.Length % 2 == 0)
return m.Value;
open = RemoveLastChar(open);
close = RemoveLastChar(close);
if (!_placeholders.Contains(key))
{
_placeholders.Add(key);
}
int index = _placeholders.IndexOf(key);
return string.Format("{0}{{{1}{2}}}{3}", open, index, format, close);
}
return m.Value;
};
_templateWithIndexes = _regex.Replace(_template, evaluator);
}
private string RemoveLastChar(string str)
{
if (str.Length > 1)
return str.Substring(0, str.Length - 1);
else
return string.Empty;
}
public static implicit operator StringTemplate(string s)
{
return new StringTemplate(s);
}
public override string ToString()
{
return _template;
}
public string Format(IDictionary<string, object> values)
{
values.CheckArgumentNull("values");
object[] array = new object[_placeholders.Count];
for(int i = 0; i < _placeholders.Count; i++)
{
string key = _placeholders[i];
object value;
if (!values.TryGetValue(key, out value))
{
value = string.Format("{{{0}}}", key);
}
array[i] = value;
}
return string.Format(_templateWithIndexes, array);
}
private IDictionary<string, object> MakeDictionary(object obj)
{
Dictionary<string, object> dict = new Dictionary<string, object>();
Type type = obj.GetType();
foreach (string propName in _placeholders)
{
var prop = type.GetProperty(propName);
if (prop != null)
dict.Add(propName, prop.GetValue(obj, null));
}
return dict;
}
public string Format(object values)
{
return Format(MakeDictionary(values));
}
public static string Format(string template, IDictionary<string, object> values)
{
return new StringTemplate(template).Format(values);
}
public static string Format(string template, object values)
{
return new StringTemplate(template).Format(values);
}
}
您还可以使用prop.GetValue(user,null)而不是prop.GetGetMethod()。Invoke(...) – 2010-03-09 22:36:27
将它包装成一个接受ANY对象的方法是否可能? – Blankman 2010-03-10 15:57:40
@Fadrian:谢谢,这是一个很好的建议! @布兰克曼:是的,它可以与任何对象一起工作。将它包装成一个通用的方法并使用'typeof(T)'(如果对象类型在编译时是已知的),或者使用'user.GetType()'获取类型为'object'的当前类型的参数运行。 – 2010-03-10 16:02:53