Iterate through Object's own Strings & Trim each

后端 未结 4 1806
梦谈多话
梦谈多话 2020-12-28 20:45

I have multiple large objects which each have about 60 strings. I have to trim all those strings, and I\'d like to do so without having to go this.mystring = this.mystring.T

相关标签:
4条回答
  • 2020-12-28 21:17

    Something like:

        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);
            }
        }
    
    0 讨论(0)
  • 2020-12-28 21:29

    Not necessary to make IEnumerable check in the props-loop and if actual instance is a IEnumerable, props are ignored. Fix for IEnumerable part:

    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 讨论(0)
  • 2020-12-28 21:30

    So to expand on this a little, I have a complex object with Lists of Lists and I wanted to traverse that and trim all of the child string objects as well. I'm posting what I did as of what I built on from @Jon did in his answer. I'm curious if there was a better way to do it or if I missed something obvious.

    The objects I have are more complex than this but it should illustrate what I was trying.

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

    Thoughts?

    0 讨论(0)
  • 2020-12-28 21:32

    Well, it's easy enough to get all the properties, and find out which ones are strings and writable. LINQ makes it even easier.

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

    You may want to only set the property if trimming actually makes a difference, to avoid redundant computations for complex properties - or it may not be an issue for you.

    There are various ways of improving the performance if necessary - things like:

    • Simply caching the relevant properties for each type
    • Using Delegate.CreateDelegate to build delegates for the getters and setters
    • Possibly using expression trees, although I'm not sure whether they'd help here

    I wouldn't take any of those steps unless performance is actually a problem though.

    0 讨论(0)
提交回复
热议问题