Текст трансформируется во все свойства объектов, есть ли лучший способ? - PullRequest
1 голос
/ 10 марта 2010

В настоящее время я делаю это:

У меня есть текст, который выглядит как:

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

Есть ли лучший способ, может быть, используя отражение как-то для обхода открытых свойств классов?

Редактировать

Можно ли сделать этот метод универсальным, чтобы я мог передать любойвозражать против этого?

Ответы [ 3 ]

1 голос
/ 10 марта 2010

Перебор всех свойств с использованием отражения и замена ключей в строке будет выглядеть примерно так:

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, если пользователи пишут ключи, используя строчные буквы (как в вашем примере).

1 голос
/ 10 марта 2010

Я написал класс 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);
    }
}
0 голосов
/ 10 марта 2010

Вы можете просмотреть свойства, вызвав typeof(User).GetProperties().

...