从开关中的字符串名称返回等效对象

Returning equivalent object from string name in switch

问题的用法有点奇怪。

当我给出对象的字符串名称时,我正在尝试 return 来自函数的 TextBox、Button 甚至 Label 对象之一,如下面的代码:

public [I don't know the type] getObjectClass(string type) {
    switch(type) {
    case "textbox":
        return new TextBox();
    break;
    case "label":
        return new Label();
    break;
}

我终于可以访问该对象了:

var obj = getObjectClass("label").Content = "I am new label!..";

首先你需要知道类型的全名。由于要创建 WPF 控件,请将控件类型名称与 'System.Windows.Controls' 连接起来。然后你需要使用 Type.GetType to create an instance of underlying type, and finally use Activator.CreateInstance 创建控件的实例。

public Control CreateControlByTypeName(string typeName) {
    string fullName = string.Format("System.Windows.Controls.{0}", typeName);
    Type type = Type.GetType(fullName);
    return (Control)Activator.CreateInstance(type);
}

您的 return 类型可能需要 dynamic

public dynamic getObjectClass(string type)

使用 ControlLabelTextBox 的最低共同祖先,不会让您访问您创建的特定 class 的属性。

请注意,此方法存在风险,因为代码不再是类型安全的。另一种方法是使用泛型,但随后您必须指定类型以及带有类型名称的匹配字符串,这违背了 getObjectClass 方法的目的。

使用动态关键字就可以了

dynamic T;
switch (xnode.Type)
{
case "text":
T = new TextBox();
T.Text = "Enter your name";

Grid.SetColumn(T, 1);
Grid.SetRow(T, row);


break;
}

但我不确定逻辑和开销问题。

简答:找到一些 class 是所有可能 return 类型的超级class。例如,考虑 System.Windows.Controls.Control

附加信息:您正在尝试创建一种创建对象的动态方式。这可能很好,例如允许您的用户创建自己的界面,但我觉得这是一种潜在的代码味道。例如,如果你想创建一个文本框然后设置它的文本,你可以这样做:

Control myTextbox = CreateObjectFromTypename("textbox");
((TextBox)myTextbox).Text = "Hello, world!";

但是既然您知道控件将被转换为文本框,为什么需要动态创建它?为什么你不能使用 TextBox myTextbox = new TextBox()

同样,我不能肯定地说你的方法不好,但我建议你小心,并确保你确实需要这种动态创作。

因此根据您当前的功能:

public [I don't know the type] getObjectClass(string type) {

类型必须是更通用的类型,例如 Control 或 Object,因为您不知道具体的 return 类型。如果您要 return 多种不同的类型(标签和文本框),则必须 return 它们都继承自的父 class。

这有点棘手

 var obj = getObjectClass("label").Content = "I am new label!..";

因为您的基础 class(return 类型)需要关联 Content 属性 才能访问 属性 而无需铸件。最简单的方法(虽然不是我最喜欢的)是施放它 ((Label) getObjectClass("label")).Content。这不会给你任何编译时间保证,尽管你 returning 的对象实际上有这个 属性。

就我个人而言,我会编写类似于

的方法
public T getObjectClass<T> () where T: new() where T : Control {
     return new T();
}

这样您就可以指定您想要的内容 return 并且泛型会自动使其成为正确的类型。当您将它作为字符串传递并让它在 运行 时失败时,您也不会 运行 遇到类似 "lable" 的问题。

我猜您想动态地向 UI 添加控件。 因为你必须在 Control 中设置一个 属性 即对于 TextBox 你必须设置 Text 属性,对于 Label 你有你设置 Content 属性。我建议采用以下方法。

在下面的示例中,我动态地向 UI 添加了一个文本框和标签。

下面代码中的重要部分是Dictionary<Type, Action<Control, String>> 属性。在此 Dictionary 中,我定义了如何根据其 Type.

为每个 Control 设置内容

注意: 我建议您以这样一种方式进行设计,即不要将实例创建和 属性 分配分成两种不同的方法。一次完成。检查带有签名 getObjectClass(string type, String content, Thickness margin) 的新方法。

XAML:

<Window x:Class="SplashScreenDemo.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="800">
    <StackPanel Name="StackPanelObj">

    </StackPanel>
</Window>

代码隐藏:

using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;

namespace SplashScreenDemo
{

    public partial class MainWindow : Window
    {
        Dictionary<Type, Action<Control, String>> SetContent = new Dictionary<Type, Action<Control, String>> 
        {
            { typeof(TextBox), (control, content) =>  (control as TextBox).Text = content},
            { typeof(Label), (control, content) =>  (control as Label).Content = content}
        };

        public MainWindow()
        {
            InitializeComponent();

            Control control = getObjectClass("textbox");
            SetContent[control.GetType()](control, "This is a textbox");

            Control control2 = getObjectClass("label");
            SetContent[control2.GetType()](control2, "This is a label");

            StackPanelObj.Children.Add(control);
            StackPanelObj.Children.Add(control2);
        }

        public Control getObjectClass(string type)
        {
            switch (type)
            {
                case "textbox":
                    return new TextBox();
                case "label":
                    return new Label();
                default:
                    return null;
            }
        }


        public Control getObjectClass(string type, String content, Thickness margin)
        {
            switch (type)
            {
                case "textbox":
                    var textBox = new TextBox();
                    textBox.Text = content;
                    textBox.Margin = margin;
                    return textBox;
                case "label":
                    return new Label();
                default:
                    return null;
            }
        }

    }

}