views:

45

answers:

2

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.Trim(). Instead, I'm looking for a way to automatically have each object discover its own strings and then perform the operation.

I know a little bit about reflection, but not enough, but I think this is possible?

Also, I'm not sure if this matters, but some string properties are read-only (only have a getter), so those properties would have to be skipped.

Help?

+3  A: 

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()
                    .GetPropertes(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.

Jon Skeet
I thought property trees are automatically cached if not in debug mode? I've read that a few times. Is this not true?
Alex
In this case, `Expression` would be a pain, even in 4.0; combining multiple `Action<T,string>` into a single delegate would work well, though.
Marc Gravell
@Alex - lambda expressions in the *source* can do some clever things, using a backing delegate field. However, AFAIK that is not true of trees built manually and compiled (`Compile`) at runtime.
Marc Gravell
@Jon, your example misses a cast from prop.GetValue to String
Alex
@Alex: Thanks, fixed.
Jon Skeet
+1  A: 

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);
        }
    }
Marc Gravell
The parameterless `Type.GetProperties()` call includes static properties, which I *suspect* shouldn't be included here.
Jon Skeet
@Jon - good point, thanks. I won't mention `IsReadable` ;p Also - you might want to check for indexers.
Marc Gravell
@Marc: Will fix :)
Jon Skeet
Could you explain why you check for prop.GetIndexParameters().Length == 0? What does this do?
Alex
@Alex: It stops it from picking up indexers.
Jon Skeet
@Alex - an indexer is something like `public string this[int i] {get {...} set {...}}`, which surfaces as a special kind of property.
Marc Gravell