dimanche 1 mars 2015

Performing operations on class properties of a certain type

The Problem


As an example, say that you receive an object that has many properties, some of which are strings. It may even be an object containing nested objects to an arbitrary level, but lets keep it flat for simplicity.



public class Foo
{
public string Prop1 { get; set; }
public string Prop2 { get; set; }
public int Prop3 { get; set; }
public string PropN { get; set; }
}


You want to make sure that you remove all '#' characters from properties of type string before passing this object on. What happens next is out of scope of the question, but it might be stored in a database, sent through the network, written to file, etc.


A simple approach


One approach (which is the one currently being used) is to have a method that just corrects each string property:



foo.Prop1 = RemoveUnwantedCharacters( foo.Prop1 );
foo.Prop2 = RemoveUnwantedCharacters( foo.Prop2 );
//etc


The problem is that whenever a new property is added to Foo, this code will need to be extended. It is quite likely that whoever added the new property to Foo will forget to remove the unwanted characters.


Using reflection


So, I am thinking of using reflection for this to parse the properties on the object and correct as needed. I seldom use reflection in production code (I use it all the time in test code) because there is often a simpler and more understandable approach and because of some of the performance concerns (which are mostly unfounded). An example of the type of approach I am thinking of is:



static void Main()
{
var foo = new Foo {Prop1 = "Prop1#", Prop2 = "Pr#op2", Prop3 = 12, PropN = "#Pro#pN#"};
DoSomething(foo);
}

static void DoSomething( Foo foo )
{
RemoveUnwantedCharacters( foo );
}

static void RemoveUnwantedCharacters( Foo foo )
{
var stringProps =
foo.GetType()
.GetProperties( BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly )
.Where( p => p.PropertyType == typeof( string ) ).ToList();
if( stringProps.Any() )
{
foreach( var stringProp in stringProps )
{
var currentValue = stringProp.GetValue( foo ).ToString();
stringProp.SetValue( foo, currentValue.Replace( "#", string.Empty ) );
}
}
}


Is reflection the right choice?


While I appreciate suggestions for improvements on the code above, it is just a throwaway example, I am more interested in whether reflection is a good approach at all.


The code strips # out of all strings. The benefit of this approach is that whenever a new string is added to Foo, the unwanted characters will be removed - nothing has to change.


Performance aside, I am interested if there is a more intuitive / elegant way to do this.


I suppose one argument is that if there was ever a string property that we didn't want to remove the unwanted characters from, then this approach wouldn't work as-is. In my case there isn't, but just for sake of argument if there was then one option is to add an attribute to the property such as [RemoveUnwantedCharacters] that we check against in the reflection parsing. This still seems preferable to the first option listed above, because the rule is added with the property and it is immediately obvious what validation is done by looking at the property, rather than in some other method. However, it does run the risk that the person adding the new property would simply forget to add the attribute.


This example shows a string, but there are similar cases for correcting ints (e.g. for all int properties of a class if the value of an int property is larger than 1000, set it to 1000), etc.


So, in summary: -



  1. Is this an appropriate case for using reflection?

  2. Is there a better way (for example what would be a good way to do this in a language such as C++ that does not support introspection)?






Aucun commentaire:

Enregistrer un commentaire