Перебирать собственные Строки и Обрезать каждую - PullRequest
10 голосов
/ 11 июля 2010

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

Я немного знаю об отражении, но недостаточно, но думаю, что это возможно?

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

Помощь

Ответы [ 4 ]

14 голосов
/ 11 июля 2010

Что ж, достаточно просто получить все свойства и выяснить, какие из них являются строками и доступны для записи. 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 для построения делегатов для методов получения и установки
  • Возможно, использовать деревья выражений, хотя я не уверен, помогут ли они здесь

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

3 голосов
/ 11 июля 2010

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

    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);
        }
    }
1 голос
/ 07 января 2013

Нет необходимости делать IEnumerable проверку в цикле props, и если фактический экземпляр равен 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));
        }
    }
}
0 голосов
/ 03 ноября 2011

Итак, чтобы расширить это немного, у меня есть сложный объект со списками списков, и я хотел пройти через это и обрезать все дочерние строковые объекты.Я публикую в своем ответе то, что сделал, основываясь на том, что сделал @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));
            }
        }
    }

Мысли

...