Итерации через объекты собственные строки и обрезать каждый

У меня есть несколько больших объектов, каждая из которых имеет около 60 строк. Мне нужно обрезать все эти строки, и я бы хотел сделать это, не запустив this.mystring = this.mystring.Trim(). Вместо этого я ищу способ автоматического обнаружения каждым объектом всех собственных строк, а затем выполнить операцию.

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

Кроме того, я не уверен, имеет ли значение это значение, но некоторые свойства строки доступны только для чтения (только у них есть getter), поэтому эти свойства должны быть пропущены.

Справка

Ответ 1

Ну, достаточно легко получить все свойства и выяснить, какие из них являются строками и доступными для записи. LINQ делает это еще проще.

var props = instance.GetType()
                    .GetProperties(BindingFlags.Instance | BindingFlags.Public)
                    // Ignore non-string properties
                    .Where(prop => prop.PropertyType == typeof(string))
                    // Ignore indexers
                    .Where(prop => prop.GetIndexParameters().Length == 0)
                    // Must be both readable and writable
                    .Where(prop => prop.CanWrite && prop.CanRead);

foreach (PropertyInfo prop in props)
{
    string value = (string) prop.GetValue(instance, null);
    if (value != null)
    {
        value = value.Trim();
        prop.SetValue(instance, value, null);
    }
}

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

Существуют различные способы повышения производительности при необходимости - например:

  • Простое кэширование соответствующих свойств для каждого типа
  • Использование Delegate.CreateDelegate для создания делегатов для геттеров и сеттеров
  • Возможно, использование деревьев выражений, хотя я не уверен, помогут ли они здесь.

Я бы не принял ни одного из этих шагов, если производительность на самом деле не проблема.

Ответ 2

Что-то вроде:

    foreach (PropertyInfo prop in obj.GetType().GetProperties(
        BindingFlags.Instance | BindingFlags.Public))
    {
        if (prop.CanRead && prop.CanWrite && prop.PropertyType == typeof(string)
            && (prop.GetIndexParameters().Length == 0)) // watch for indexers!
        {
            var s = (string)prop.GetValue(obj, null);
            if (!string.IsNullOrEmpty(s)) s = s.Trim();
            prop.SetValue(obj, s, null);
        }
    }

Ответ 3

Не нужно делать IEnumerable проверку петли реквизита, и если фактический экземпляр является IEnumerable, реквизиты игнорируются. Исправить для части IEnumerable:

private void TrimWhitespace(object instance)
{
    if (instance != null)
    {
        if (instance is IEnumerable)
        {
            foreach (var item in (IEnumerable)instance)
            {
                TrimWhitespace(item);
            }
        }

        var props = instance.GetType()
                .GetProperties(BindingFlags.Instance | BindingFlags.Public)
            // Ignore indexers
                .Where(prop => prop.GetIndexParameters().Length == 0)
            // Must be both readable and writable
                .Where(prop => prop.CanWrite && prop.CanRead);

        foreach (PropertyInfo prop in props)
        {
            if (prop.GetValue(instance, null) is string)
            {
                string value = (string)prop.GetValue(instance, null);
                if (value != null)
                {
                    value = value.Trim();
                    prop.SetValue(instance, value, null);
                }
            }
            else 
                TrimWhitespace(prop.GetValue(instance, null));
        }
    }
}

Ответ 4

Итак, чтобы немного расшириться, у меня есть сложный объект со списками списков, и я хотел бы пройти его и обрезать все дочерние строковые объекты. Я отправляю то, что я сделал, из того, что я построил с @Jon, в его ответе. Мне любопытно, есть ли лучший способ сделать это, или если я пропустил что-то очевидное.

Объекты, которые у меня есть, более сложны, чем это, но это должно проиллюстрировать, что я пытался.

public class Customer
{
  public string Name { get; set; }
  public List<Contact> Contacts { get; set; }
}

public class Contact
{
  public string Name { get; set; }
  public List<Email> EmailAddresses {get; set;}
}

public class Email
{
  public string EmailAddress {get; set;}
}


    private void TrimWhitespace(object instance)
    {
        if (instance != null)
        {
            var props = instance.GetType()
                    .GetProperties(BindingFlags.Instance | BindingFlags.Public)
                // Ignore indexers
                    .Where(prop => prop.GetIndexParameters().Length == 0)
                // Must be both readable and writable
                    .Where(prop => prop.CanWrite && prop.CanRead);

            foreach (PropertyInfo prop in props)
            {
                if (instance is IEnumerable)
                {
                    foreach (var item in (IEnumerable)instance)
                    {
                        TrimWhitespace(item);
                    }
                }
                else if (prop.GetValue(instance, null) is string)
                {
                    string value = (string)prop.GetValue(instance, null);
                    if (value != null)
                    {
                        value = value.Trim();
                        prop.SetValue(instance, value, null);
                    }
                }
                else 
                    TrimWhitespace(prop.GetValue(instance, null));
            }
        }
    }

Мысли?