【问题标题】:Dynamic Casting in C#C# 中的动态转换
【发布时间】:2009-07-11 22:38:39
【问题描述】:

想象一下下面的情况。我有一个如下的 XML 文档,

<Form>
    <Control Type="Text" Name="FirstName" />
    <Control Type="DateTime" Name="DateOfBirth" />
    <Control Type="Text" Name="PlaceOfBirth" />
</Form>

我有一个名为 Control 的抽象类,它有一个名为 Process 的抽象方法,它接受一个 HttpRequest 参数。我还有另外两个从 Control 派生的类,称为 TextControl 和 DateTimeControl。 Text 和 DateTime 都覆盖 Process 方法以提供它们自己的实现。

我还有一个 Form 类,它有一个采用 HttpRequest 类型的单个参数的 Process 方法,以及一个采用 XmlDocument 类型的单个参数的构造函数。

创建了一个新的 Form 实例,上面的 Xml 通过 XmlDocument 参数传入(我们如何从字符串到 XmlDocument 无关紧要)。然后,我在刚刚创建的 Form 实例上调用 Process 方法,并按预期传入一个 HttpRequest 类型的参数。

到目前为止一切顺利。现在进入问题。

为了使控件的处理可扩展,我希望能够将类映射到控件类型。

例如。

Form.RegisterControl("Text", Text)
Form.RegisterControl("DateTime", DateTimeControl)

在 Form 的 Process 方法中,我想迭代文档中的每个 Control 节点(如何做到这一点再次无关紧要)并根据我们的 RegisterControl 方法注册的类实例化与其类型匹配的类的实例.在这个阶段,我可以指定它们是从 Control 派生的,但不能明确指定它们的类型。因为它们都是从 Control 派生的,所以我想调用我知道会实现的 Process 方法。

这甚至可能吗?如果是这样,我会怎么做?

【问题讨论】:

    标签: c# oop abstract-class


    【解决方案1】:

    (这个答案在某些方面是两个不同的答案,具体取决于您问题的含义。希望 其中的一个 有帮助,无论如何 :)


    可能最好的方法是传入一个参数,该参数是一个函数,可用于在正确的时间创建新控件。如果您使用的是 C# 3,这很简单:

    Form.RegisterControl("Text", () => new Text())
    

    或者,您可以将其设为具有两个约束的泛型方法:一个用于作为控件,另一个用于具有无参数构造函数。

    public void RegisterControl<T>(string name) where T : Control, new()
    

    然后调用它:

    Form.RegisterControl<Text>("Text");
    Form.RegisterControl<DateTimeControl>("DateTime");
    

    RegisterControl 必须记住 typeof(T) 在它使用的任何存储中,但至少可以合理地确定 Activator.CreateInstance(Type) 稍后会工作 - 并且您将进行编译时检查。

    我个人喜欢第一种形式的灵活性——如果你传入一个委托,它可以选择使用单例,或者可能是一些内部甚至私有构造函数(取决于它被调用的位置)。你也可以使用一个接受适当数据的委托:

    Form.RegisterControl("Text", data => new Text(data));
    

    你不能用泛型约束来表达那种构造函数,而且以后调用起来也相对困难。


    编辑:我自己和 Mehrdad 都可能误解了这个问题。根据控件类型,您实际上有不同的RegisterControl 重载吗?如果是这样,那么在执行时直接调用正确重载的唯一方法是使用反射或使用 C# 4 中的动态类型。

    另一种选择是使用双重分派 - 在控件本身中放置一个方法,该方法知道如何使用表单注册自己。这将在接口或基类中指定,但在具体子类中被覆盖。所以你的代码目前是:

    Form.RegisterControl("Text", control);
    

    会变成:

    control.RegisterWith(Form, "Text");
    

    然后可以毫无问题地调用正确的重载。

    基本上,您需要记住重载解析是在编译时执行的,而覆盖解析是在执行时执行的——因此,如果您想使某些东西动态化,请尝试使用多态来处理它。

    【讨论】:

    • 谢谢乔恩!第一个答案应该是正确的(您已正确假设)。我现在正在努力解决它!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-09-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-10-06
    相关资源
    最近更新 更多