方法重载和多态

Posted

技术标签:

【中文标题】方法重载和多态【英文标题】:method overloading and polymorphism 【发布时间】:2010-11-14 20:02:01 【问题描述】:

我正在编写一个 .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) 一个好主意吗?

【问题讨论】:

【参考方案1】:

这个问题上的“dispatch”标签非常贴切:你想要的叫做“multiple dispatch”。 C#(像大多数主流语言一样)仅支持“单一调度”,其中要执行的方法仅在您调用该方法的对象的(运行时)类型上选择,而不是在其参数的(运行时)类型上选择。

访问者模式通常可以用来解决这个问题。这个想法是你给DocumentFieldBase一个方法(你在具体的子类中重写),它调用Control上的一个方法(在具体的子类中也被重写)来完成实际的工作。

不幸的是,Control 类的源代码可能不在您的控制范围内*...所以您将不得不求助于一个更加骇人听闻的解决方案。 this question 的公认答案提供了一个使用反射的答案。

*扩展方法只是静态方法的语法糖,因此在编译时解析,在这种情况下没有用。

【讨论】:

回答是因为你给了我的痛苦一个名字:) 我经常读到双重调度和访客模式,并且有一种预感,那就是我正在处理的问题(因此“调度”标签你提到)。您还设法提炼出单调度和多调度之间的关键区别,这是我读过的许多关于该主题的文章都无法做到的。【参考方案2】:

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

在 C# 4 中,您可以将变量声明为动态变量,并在执行时将其全部整理出来:

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

显然,目前这并没有多大帮助(除非您使用的是 VS2010b1)。

一种选择是使用从TypeAction&lt;object&gt; 的映射,但随后您会遇到继承问题...(您可能必须继续处理从具体类型到对象的类型层次结构,直到找到地图中的一个条目)。您还需要在操作中转换为正确的类型:(

【讨论】:

【参考方案3】:

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

【讨论】:

卡尔,我没有考虑过这种方法。我并不是非常热衷于在我的域层中引入对 System.Web.UI 的依赖,但我可能会写一个同样的 POC。谢谢!

以上是关于方法重载和多态的主要内容,如果未能解决你的问题,请参考以下文章

Java 方法重载和多态

重载和重写有啥区别

java多态的2种表现形式 方法重载和方法覆盖

多态性和方法重载

方法重载和多态性

重写和重载的区别