views:

419

answers:

3

I'm writing a .NET web application in which administrators can customize the various data entry forms presented to their users. There are about half a dozen different field types that admins can create and customize (i.e. text, numeric, dropdown, file upload). All fields share a set of base attributes/behaviors (is the field required? Will it have a default field value?). There are also a series of field specific attributes/behaviors (i.e dropdown has a data source attribute, but text field does not). I'm leaving out many other characteristics of the problem domain for simplicity's sake.

The class hierarchy is straightforward: An abstract superclass that encapsulates common behaviors/attributes and about half a dozen concrete subclasses that deal with field specific stuff.

Each field type is rendered (i.e. mapped to) as a specific type of .NET server control, all of which derive from System.Web.UI.Control.

I created the following code to map values between the field domain objects and their corresponding UI control:

public static void Bind(Control control, IList<DocumentFieldBase> fieldBaseList)

     foreach (DocumentFieldBase fieldBase in fields){

            if (typeof (DocumentFieldText).IsInstanceOfType(fieldBase)){
                TextBox textbox = (TextBox) control;
                textbox.Text = (fieldBase as DocumentFieldText).GetValue();
            }

            if (typeof (DocumentFieldDropDown).IsInstanceOfType(fieldBase)){
                DropDown dropDown= (DropDown) control;
                dropDown.Text = (fieldBase as DocumentFieldSelectOne).GetValue().Text;
                dropDown.DataSource= (fieldBase as  DocumentFieldSelectOne).DataSource;
                dropDown.Id= (fieldBase as DocumentFieldSelectOne).GetValue().Id;
            }

            //more if statements left out for brevity
      }
}

I want to ditch those ungodly if statements that perform type checking. The approach I was shooting for was to create a method overload for each combination of field/control using subclass typing. For example:

public static void Bind(TextBox control, DocumentFieldText fieldText){
 //some implementation code
}
public static void Bind(DropDown control, DocumentFieldDropDown fieldDropDown){
 //some implementation code
}

I was hoping that I could then rely on .NET to call the appropriate overload at runtime using the specific subclass being used: For example:

foreach (DocumentFieldBase field in fields){
  Control control = FindControl(field.Identifier);
  Bind(control, field)
}

Unfortunately, the compiler chokes when I try this: Argument '1': cannot convert from 'System.Web.UI.Control' to 'TextBox'.

If I have to cast the first argument to TextBox, I'm back to performing type checking myself and defeats the whole purpose of this exercise.

Is what I'm trying to achieve a) possible and b) a good idea?

+1  A: 

Prior to C# 4, all overloading is done at compile time. You have to use double dispatch or the visitor pattern to effectively overload at execution time, and that gets messy quickly.

In C# 4, you could declare a variable as dynamic and let it all get sorted out at execution time:

foreach (DocumentFieldBase field in fields){
  dynamic control = FindControl(field.Identifier);
  Bind(control, field)
}

Obviously that's not much help at the moment though (unless you're using VS2010b1).

One option is to use a map from Type to Action<object> but then you get inheritance issues... (you'd potentially have to keep working up the type hierarchy from the concrete type up to object until you found an entry in the map). You'd also still need to cast to the right type within the action :(

Jon Skeet
+2  A: 

The "dispatch" tag on this question is quite appropriate: what you want is called "multiple dispatch". C# (like most mainstream languages) only supports "single dispatch", where the method to be executed is selected solely on the (runtime) type of the object you call the method on, not on the (runtime) type of its arguments.

The visitor pattern can often be used to work around this. The idea is that you give DocumentFieldBase a method (that you override in concrete subclasses) which calls a method on Control (also overridden in concrete subclasses) that does the actual work.

Unfortunately, the source code of the Control class is probably not under your control*... so you'll have to resort to an even more hackish solution. The accepted answer to this question provides one that uses reflection.

*Extension methods are just syntactic sugar for static methods, and are thus resolved at compile time and of no use in this scenario.

Thomas
Answer awarded because you gave my pain a name :) I've often read about double dispatch and the Visitor pattern and had a hunch that was the problem I was dealing with (hence the "dispatch" tag you mention). You've also managed to distill the crucial differences between single dispatch and multiple dispatch, something many of the articles I've read on the subject could not do.
Dirk
A: 

Couldn't you have an abstract, non-static Bind() method in DocumentFieldBase, then do the downcasting inside each concrete class's implementation of it? Each DocumentFieldBase class knows what type of Control it's getting, doesn't it?

Carl Manaster
Carl, I didn't consider this approach. I'm not terribly keen on introducing a dependency on System.Web.UI in my domain layer, but I may write a quick of POC just the same. Thanks!
Dirk