【问题标题】:Text transforms on all of objects properties, is there a better way?对所有对象属性进行文本转换,有更好的方法吗?
【发布时间】:2010-03-09 22:07:06
【问题描述】:

目前我正在这样做:

我的文字看起来像:

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();
}

有没有更好的方法,也许使用反射来循环遍历类的公共属性?

编辑

是否可以将此方法设为通用,以便我可以将任何对象传递给它?

【问题讨论】:

    标签: c# reflection string templating


    【解决方案1】:

    使用反射遍历所有属性并替换字符串中的键大致如下所示:

    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 来检查属性是否有getter,然后调用getter 来读取值。只需使用 ToString 将值转换为字符串,这可能适用于原始类型(取决于所需的行为)。这是区分大小写的,因此如果用户使用小写字母(如您的示例)编写密钥,您可能希望使用 ToLower

    【讨论】:

    • 你也可以使用 prop.GetValue(user, null) 代替 prop.GetGetMethod().Invoke(...)
    • 是否可以将其包装成一个接受任何对象的方法?
    • @Fadrian:谢谢,这是个好建议! @Blankman:是的,它可以与任何对象一起使用。要么将其包装成一个泛型方法并使用typeof(T)(如果在编译时知道对象类型),要么使用user.GetType() 在运行时获取object 类型的当前参数类型。
    【解决方案2】:

    我写了一个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);
        }
    }
    

    【讨论】:

      【解决方案3】:

      您可以通过调用typeof(User).GetProperties() 来循环访问属性。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2018-12-19
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-07-18
        • 2011-05-26
        • 2020-08-31
        • 2013-01-09
        相关资源
        最近更新 更多