方法重载和多态性

5
我正在编写一个.NET Web应用程序,管理员可以自定义呈现给用户的各种数据输入表单。管理员可以创建和自定义大约半打不同类型的字段(例如文本、数字、下拉列表、文件上传)。所有字段共享一组基本属性/行为(字段是否必填?它是否有默认字段值?)。还有一系列特定于字段的属性/行为(例如下拉列表具有数据源属性,但文本字段没有)。出于简单起见,我省略了许多问题域的其他特征。
类层次结构很简单:一个抽象超类封装了常见的行为/属性,以及大约半打处理特定于字段的内容的具体子类。
每种字段类型都呈现为特定类型的.NET服务器控件(即映射到),所有这些控件都派生自System.Web.UI.Control。
我创建了以下代码来映射字段域对象和其对应的UI控件之间的值:
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
      }
}

我希望摆脱那些进行类型检查的可怕 if 语句。我的方法是使用子类类型为每个字段/控件组合创建方法重载。例如:

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

我希望能够依靠.NET在运行时使用正在使用的特定子类来调用相应的重载方法。例如:
foreach (DocumentFieldBase field in fields){
  Control control = FindControl(field.Identifier);
  Bind(control, field)
}

很不幸,当我尝试这样做时,编译器报错了:

参数'1':无法将'System.Web.UI.Control'转换为'TextBox'。

如果我必须将第一个参数强制转换为TextBox,那么我又要自己执行类型检查,这就打败了整个练习的目的。

我试图实现的目标是a)可能的和b)一个好主意吗?

3个回答

7

在 C# 4 之前,所有的重载都是在编译时完成的。你必须使用双重调度或访问者模式来有效地实现执行时间重载,这很快就会变得混乱。

在 C# 4 中,您可以将变量声明为 dynamic,并在执行时让它们全部排序:

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

显然目前这并没有什么帮助(除非你在使用VS2010b1)。

一个选项是使用从TypeAction<object>的映射,但这样会涉及到继承问题...(你可能需要一直向上工作,从具体类型一直到object,直到在映射中找到一个条目)。你仍然需要在action内部进行正确类型的转换 :(


6
这个问题中的“dispatch”标签很合适:你想要的是叫做“多重分派”。C#(像大多数主流语言一样)只支持“单重分派”,其中要执行的方法仅根据调用该方法的对象的类型(运行时)而选择,而不是根据其参数的类型(运行时)而选择。
访问者模式通常可以用来解决这个问题。思路是给DocumentFieldBase一个方法(在具体的子类中覆盖),该方法调用Control上的一个方法(在具体的子类中也被覆盖)来执行实际的工作。
不幸的是,Control类的源代码可能不受您控制*……因此,您将不得不采取更加hackish的解决方案。在这个问题的被接受的答案提供了使用反射的方法。
*扩展方法只是静态方法的语法糖,因此在这种情况下,在编译时解析,并且对此场景没有用处。

1
答案被授予,因为你给了我的痛苦一个名字 :) 我经常阅读关于双重分派和访问者模式的文章,并且有一种预感这就是我正在处理的问题(因此你提到的“分派”标签)。你还成功地概括了单重分派和多重分派之间的关键差异,这是许多我阅读过的文章所无法做到的。 - Mitch A

0

在DocumentFieldBase中,您不能有一个抽象的、非静态的Bind()方法,然后在每个具体类的实现中进行向下转型吗?每个类都知道它正在获取什么类型的 Control, 不是吗?


Carl,我没有考虑过这种方法。我并不是特别热衷于在我的领域层中引入对 System.Web.UI 的依赖,但我可能会写一个快速的 POC。谢谢! - Mitch A

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接