- Creating a custom class that inherits the class you want to extend and add your function. This would force you to update all declarations of that type
- Creating a shared function in a common static class. This can add make for some ugly code that doesn't look very object oriented
Extension methods, in .Net 3.0 (or later), add a third option to the list. They allow a function to attatch to all existing objects of a type. This enables anyone to become a language designer and fix all of the holes Microsoft left just to annoy you. Lets take a look at a couple examples.
Suppose a legacy system requires a CSV input and the .Net application contains a list of strings. The .Net 2.0 solution might look something like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | public static class Common { public static string ToCSV(List< string > list, char delimiter) { StringBuilder sb = new StringBuilder(); for ( int idx = 0; idx < list.Count; idx++) { sb.Append(delimiter); } sb.Append(list[idx].ToString()); } return sb.ToString(); } } ... // Then run ... string csv = Common.ToCSV(<nameoflist>, ',' ); |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | public static class Extensions { public static string ToCSV( this List< string > list, char delimiter) { StringBuilder sb = new StringBuilder(); for ( int idx = 0; idx < list.Count; idx++) { sb.Append(delimiter); } sb.Append(list[idx].ToString()); } return sb.ToString(); } public static void FromCSV( this List< string > list, string csv, char delimiter) { list.AddRange(csv.Split( new char [] { delimiter })); } } class Program { static void Main( string [] args) { List< string > strings = new List< string >(); strings.Add( "Bob" ); strings.Add( "Jane" ); strings.Add( "Jack" ); strings.Add( "Mike" ); strings.Add( "Mary" ); // Call the Extension Method string csv = strings.ToCSV( ',' ); Console.WriteLine(csv); // Lets verify that the opposite way works List< string > newlist = new List< string >(); // Call the Extension Method newlist.FromCSV(csv, ',' ); for ( int idx = 0; idx < newlist.Count; idx++) { Console.WriteLine(newlist[idx]); } } } |
Extension methods are a powerful tool for creating utility libraries and adding common functionality to built-in or inaccessible object types. Like the adage, with great power comes great responsibility, this tool requires great responsibility. These methods can make maintaining the code more difficult for new maintainers. A developer can change inner workings and/or the output of the method, resulting in a debugging nightmare. Trying to locate the problem can become more difficult if someone assumes that the method is not built-in and the error is not explicitly in the method or at the call.
It would advisable to use extension methods for only generic cases where the method would be used in many different projects.
Now, lets take a section of the previous posts code and extend it to use a new "Quad" extension method. I'll discuss it further in a moment.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 | public static class Extensions { /// <summary> /// Stopwatch Extension Method: /// Starts, executes function over iterations /// Returns the time span. /// </summary> public static TimeSpan Time( this Stopwatch sw, Action func, int times) { sw.Reset(); sw.Start(); for ( int idx = 0; idx < times; idx++) { func(); } sw.Stop(); return sw.Elapsed; } public static double [] Quad( this double [] p) { double [] x = new double [2]; double a = p[0], b = p[1], c = p[2]; x[0] = ((b * -1) + Math.Sqrt(b * b + 4 * a * c))/(2 * a); x[1] = ((b * -1) - Math.Sqrt(b * b + 4 * a * c))/(2 * a); return x; } } class Program { static void Main( string [] args) { Stopwatch stopwatch = new Stopwatch(); int max = 1000000; double [][] numbersP = new double [max][]; double [][] numbers = new double [max][]; Random rand = new System.Random(1042); Parallel.For(0, max, x => { double [] item = new double [3]; item[0] = rand.Next(500); item[1] = rand.Next(500); item[2] = rand.Next(500); numbers[x] = item; }); TimeSpan ts = stopwatch.Time(() => { Parallel.For(0, max, x => { // Make the call to the extension method numbersP[x] = numbers[x].Quad(); }); }, 1); Console.WriteLine( String.Format( "Parallel.For RunTime: {0:00}:{1:00}:{2:00}.{3:00}" , ts.Hours, ts.Minutes, ts.Seconds, ts.Milliseconds/10)); Console.Read(); } } |
Using Quad in this fashion is definitely not an optimal approach/reason to using extension methods. In this case Quad does not really extend the type double[], therefore one of the other options listed above is definitely better. For example, adding this function to a common math class. This will be the hard part for new developers to understand and adds to my reasoning for using them sparingly and after some diligence.
This will be an important tool for experienced programmers. This has the makings of many sleepless nights for me wondering when I am going to misuse of extension methods like the example above.
No comments:
Post a Comment