C# 中的动态转换

发布于 2024-07-26 22:41:48 字数 1098 浏览 4 评论 0原文

想象一下下面的情况。 我有一个 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 实例,并通过 XmlDocument 参数传入上述 Xml(与我们如何从字符串获取 XmlDocument 无关)。 然后,我在刚刚创建的 Form 实例上调用 Process 方法,并按预期传入 HttpRequest 类型的参数。

到目前为止一切都很好。 现在进入问题。

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

例如。

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

在 Form 的 Process 方法中,我想迭代文档中的每个 Control 节点(如何执行此操作再次无关紧要),并根据我们的 RegisterControl 方法注册的类实例化与其类型匹配的类的实例。 在这个阶段,我可以指定它们是从 Control 派生的,但无法显式指定它们的类型。 因为它们都是从 Control 派生的,所以我想调用 Process 方法,我知道该方法将被实现。

这可能吗? 如果是这样我该怎么办?

Picture the following situation. I have an XML document as follows,

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

I have an abstract class called Control with a single abstract method called Process which takes a single parameter of HttpRequest. I also have two other classes that are derived from Control called TextControl and DateTimeControl. Both Text and DateTime override the Process method to provide their own implementations.

I also have a Form class which has a Process method taking a single parameter of type HttpRequest, and a constructor which takes a single parameter of type XmlDocument.

A new instance of Form is created and the above Xml is passed in via the XmlDocument parameter (how we get from the string to an XmlDocument is irrelevant). I then call the Process method on the instance of Form I just created and pass in a parameter of type HttpRequest as expected.

All good so far. Now onto the question.

To make the processing of Controls extensible I would like to be able to map Classes to control types.

eg.

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

Within the Process method of Form I would like to itterate over each Control node in the document (how to do this is again irrelevant) and instantiate an instance of the class which matches its type based on the Classes registered by our RegisterControl method. I would be able at this stage to specify that they are derived from Control but could not explicitly specify their type. Because they are both derived from Control I want to call the Process method which I know will be implemented.

Is this even possible? If so how would I go about it?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

微凉徒眸意 2024-08-02 22:41:48

(这个答案在某种程度上是两个不同的答案,具体取决于你问题的含义。无论如何,希望其中的一部分是有帮助的:)


可能最好的方法是传递一个参数,它是函数可用于在正确的时间创建新控件。 如果您使用的是 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));

您无法使用通用约束来表达这种构造函数,而且以后调用起来也相对困难。


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

另一种选择是使用双重分派 - 在控件本身中放置一个知道如何注册的方法本身有一个形式。 这将在接口或基类中指定,但在具体子类中被覆盖。 因此,您的代码当前:

Form.RegisterControl("Text", control);

将变为:

control.RegisterWith(Form, "Text");

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

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

(This answer is in some ways two different answers, depending on the meaning of your question. Hopefully one part of it is helpful, anyway :)


Probably the best way is to pass in an argument which is a function which can be used to create the new control at the right time. If you're using C# 3, this is as simple as:

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

Alternatively, you could make it a generic method with two constraints: one for being a control, and one for having a parameterless constructor.

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

then call it with:

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

RegisterControl would have to remember typeof(T) in whatever storage it's using, but at least then it could be reasonably sure that Activator.CreateInstance(Type) would work later on - and you'd have compile-time checking.

Personally I like the flexibility of the first form though - if you're passing in a delegate, that can choose to use a singleton, or perhaps some internal or even private constructor (depending on where it's being called from). You could also use a delegate which took the appropriate data, too:

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

You can't express that sort of constructor with a generic constraint, and it would be relatively hard to invoke later anyway.


EDIT: It's possible that both myself and Mehrdad have misinterpreted the question. Do you actually have different overloads of RegisterControl based on the control type? If so, then the only ways of directly calling the right overload at execution time are to either use reflection or use dynamic typing in C# 4.

Another alternative would be to use double dispatch - put a method in the control itself which knows how to register itself with a form. This would be specified in the interface or base class, but overridden in the concrete subclasses. So your code which is currently:

Form.RegisterControl("Text", control);

would become:

control.RegisterWith(Form, "Text");

That could then call the correct overload with no issues.

Basically you need to remember that overload resolution is performed at compile time, but override resolution is performed at execution time - so if you want to make something dynamic, try to approach it with polymorphism.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文