Monday, January 14, 2008

ASP.NET AJAX: Half-way between createDelegate() and createCallback()

I stumbled on a use of Function.createDelegate() method today that seems to be a cross between Function.createDelegate() and Function.createCallback().

I had the need to pass a context to a handler function, with the context being a JSON object containing the state of a few variables.  I tried using createCallback(), which is designed for that purpose, but it did not seem to work well because I was not using it together with a DOM event — just with an internal function call.

So I turned to createDelegate(), using the JSON object as the this argument, and it worked quite nicely.

An example of this syntax is as follows.  The code assigns a function to the variable fn that (when called) displays an alert box containing the element number and id of a DIV with an ID of "ThisOne".  There are certainly more useful uses for this method, but it should provide a clear example of how it works.  The key point here is the function() assignment that uses this to utilize the values passed in the delegate.

var a = document.getElementsByTagName("div");
var fn;

for (var x=0; x < a.length; x++) {
    if (a[x].id == "ThisOne") {
        fn = Function.createDelegate({
            e: a[x],
            num: x
        }, function () {
            alert("DIV #"+this.num+": "+this.e.id);
        });
    }
}

fn();

If you're not sure about the value of the above code, ask yourself, "How would I code the same thing without the createDelegate()?"  Also ask yourself how hard it would be to pass the DOM element itself to your function, as I have done with createDelegate().

Would you use the Function() constructor that allows you to create a dynamic function from a string value?  (Bad idea.)

Would you use global variables?  (Bad idea.)

If your answer is that you'd use apply(), then you're doing the same thing that createDelegate() does internally.  If you didn't think of that before you read this, then you can't claim credit for thinking of it.  ;-)

Wednesday, January 2, 2008

ChangeAll extension method alters every item in an iCollection(Of T)

My favorite feature of the new .NET 3.5 framework is extension methods.  Ever since I heard they would be available in this version of the .NET framework I have been designing functions in a way that they could be easily converted to extension methods.

For the unenlightened, extension methods are a new feature of the .NET 3.5 framework that allows you to extend any class — whether it is a built-in class, a third-party class, or your own class.

When you create an extension method, you can call the new method as if it were a native method of the class.

As an easy-to-understand example, I created an extension of the .NET String class called SuperTrim().  The method works like the built-in Trim() method, but instead of just trimming spaces from the front and back of a string, it trims any kind of control characters (any character with an ASCII code of less than 32, plus a few others).

After creating the extension method, I can apply SuperTrim() to an string in exactly the same way I would apply a regular Trim():

newString = oldString.SuperTrim()

Now that's cool! 

Last week I came upon a scenario in a program where I had to manipulate all of the items in a collection, so I tried using the built-in ForEach() method.  The trouble is that ForEach() is designed to do something with all the items in a collection, but not to change all of the items in a collection.

ForEach() passes each item, one by one, to your specified function, but it explicitly only passes the item by value, not by reference.  If your function changes the value passed, it is not changed in the collection — only within the scope of your function.

ForEach() also does not pass an index number or any other value that could help determine which item in the collection to change, so any attempt to use ForEach() to change items in the original collection would be a messy affair.

In response, I created an extension to iCollection called ChangeAll().  You pass a function to ChangeAll() that will receive each item, one by one, and the return value from your function will become the new value of that item in the collection.

Incidentally, ChangeAll() works great with the new lambda expressions — another wonderful .NET 3.5 feature!

Here is a sample usage of ChangeAll(), which trims all items in a collection (using a lambda expression):

myColl.ChangeAll(Function(value As String) Trim(value))

Pretty nice, eh?

If you don't want to use a lambda, you can just as easily use a regular function, like this:

Function TrimIt(ByVal value As String) As String
    Return Trim(value)
End Function

myCollection.ChangeAll(AddressOf TrimIt) 

There are lots of other uses, besides trimming every item in the collection.  The point is that now it is very easy to change each item of a collection, in a way that is simple and easy to read/understand — thanks to the new extension methods.

When creating ChangeAll() I first tried extending iEnumerable(Of T), but found that the base class does not have sufficient functionality to do what I needed.  So I then extended iCollection(Of T) instead, which can do some basic manipulation of the collection's items.

So without further adieu, here is the code, in its entirety.  This is the entire contents of a file I call "ColectionExtensions.vb" (you can give the file any name).  It includes inline documentation (yay!).

Option Explicit On
Option Strict On

Imports
System.Runtime.CompilerServices

Public Module CollectionExtensions

  ''' <summary>
  ''' Executes a transformation function on each item in
  ''' an ICollection(T) generic collection, replacing
  ''' each item with the return value.
  ''' </summary>
  ''' <typeparam name="T">
  ''' The type contained in Collection and that is passed
  ''' to ChangeFunction, as well as the type that must be
  ''' returned from ChangeFunction.
  ''' </typeparam>
  ''' <param name="Collection">
  ''' The collection on which ChangeFunction will be
  ''' applied to each item.
  ''' </param>
  ''' <param name="ChangeFunction">
  ''' Each item in Collection will be passed to this
  ''' function, and the return value will replace the
  ''' original item in the collection. If you wish an
  ''' item to remain unchanged, this function must
  ''' return the item's original value.
  ''' </param>
  ''' <returns>
  ''' Returns the collection, so this method may be
  ''' daisy-chained.
  ''' </returns>
  <Extension()> _
  Public Function ChangeAll(Of T)( _
    ByVal Collection As ICollection(Of T), _
    ByVal ChangeFunction As Func(Of T, T)) _
  As ICollection(Of T)

    If (Not Collection.IsReadOnly) Then
      Dim newCollection(Collection.Count - 1) As T

      Collection.CopyTo(newCollection, 0)
      Collection.Clear()

      For Each item As T In newCollection
        Collection.Add(ChangeFunction(item))
      Next

    End If

    Return Collection
  End Function

End Module

Obviously, all of the above is written in VB, but exactly the same functionality can be created in C# with a few tweaks in syntax.

I hope that this extension can be useful to you, but more importantly, that it can demonstrate the usefulness of the technique, so that you can similarly extend classes in your applications.