【问题标题】:How can you cast to a type using the type name as a string?如何使用类型名称作为字符串转换为类型?
【发布时间】:2011-01-28 19:10:17
【问题描述】:

好的,我整天都在思考这个想法,我已经到了我承认我完全不知道的部分。可能我正在做的事情很愚蠢,有更好的方法,但这就是我的想法带给我的地方。

我正在尝试使用通用方法在 WinForms 中加载表单:

protected void LoadForm<T>(ref T formToShow, bool autoLoaded) where T : FormWithWorker, new()
{
    // Do some stuff
}

表单由 ToolStripMenuItem 加载(通过选择项或使用打开 Windows 菜单项)。它们是延迟加载的,因此在 MDI 父级中存在表单字段,但在需要它们之前它们为空。我有一个用于处理所有菜单项单击的 ToolStripMenuItem_Click 的通用方法。除了 ToolStripMenuItem 的名称与为它们对应的表单类名称选择的模式相匹配之外,该方法无法真正知道正在调用哪个表单。因此,使用 ToolStripMenuItem 的名称,我可以推测所请求的表单类型的名称以及分配用于存储该表单引用的私有字段的名称。

使用它,我可以使用具有硬编码类型和字符串匹配的增长/收缩 switch 语句来调用具有特定类型集的方法(不受欢迎),或者我可以使用反射来获取字段并创建实例方式。我的问题是,System.Activator.CreateInstance 提供了一个无法转换为我需要的类型的 ObjectHandler。这是我目前所拥有的 sn-p:

string formName = "_form" + ((ToolStripMenuItem)sender).Name.Replace("ToolStripMenuItem", "");
string formType = formName.Substring(1);

FieldInfo fi = this.GetType().GetField(formName, BindingFlags.NonPublic | BindingFlags.Instance);

FormWithWorker formToLoad = (FormWithWorker)fi.GetValue(this);
if (formToLoad == null)
{
    formToLoad = (????)System.Activator.CreateInstance("MyAssemblyName", formType);
}

this.LoadForm(ref formToLoad, false);
fi.SetValue(this, formToLoad);

我知道用于 (????) 的类型的字符串名称,但在编译时我不知道类型,因为它会更改。我已经尝试了很多方法来让这个演员/实例化工作,但没有一个成功。我非常想知道是否有可能只知道类型作为字符串来执行这样的转换。我尝试使用Type.GetType(string, string) 执行强制转换,但编译器不喜欢它。如果有人因为我只是愚蠢地这样做而对如何动态加载表单有不同的想法,请告诉我。

【问题讨论】:

  • 制作 formToLoad 对象,并在 LoadForm 和 SetValue 点将其转换为 FormWithWorker?

标签: c# winforms reflection casting


【解决方案1】:

这个问题通常通过转换为所有潜在类型的公共基类或接口来解决。

在 C# 4 中,您还可以将其分配给 dynamic 变量以保存返回值并在其上调用任意方法。这些方法将是后期绑定的。但是,我更愿意尽可能坚持前一种解决方案。

【讨论】:

  • 赞成。我使用界面做了一些非常相似的事情,而且效果很好。
  • 我试过这个方法,但我遇到的问题是它最终分配的动态类型是基类而不是实际类,所以在调用 SetValue 方法时,它抛出了一个类型-铸造异常。
  • @Joel 正如另一个答案指出的那样,您应该使用返回对象本身的重载。显然,您使用的重载对于激活 .NET 远程处理对象很有用。
  • 今天早上我对这个话题有了新的认识,我无法得到@Andras Vass 的工作答案的原因是我在 CreateInstance 中使用了错误的命名空间/类型。一旦我意识到这个错误,他的想法就非常有效。不过,我为你的回答 +1,因为这对我来说很有意义。
【解决方案2】:

您最好使用带有Typeother overload 并使用例如Type.GetType(string).

FormWithWorker formToLoad = (FormWithWorker)fi.GetValue(this);
if (formToLoad == null)
{
    formToLoad =
      (FormWithWorker)System.Activator.CreateInstance(Type.GetType("MyNamespace.MyFormType"));
}

【讨论】:

  • 我记得有一次这篇文章特别提到了在System.Activator.CreateInstance 上使用.UnWrap() 方法。现在不说了,但应该是因为我的问题的解决方案是用基类的类型声明私有字段,然后调用CreateInstance("namespace","type").Unwrap()。这创建了正确的类型并允许该方法正确实例化。
  • 如果必须强制转换,为什么这很有用?
【解决方案3】:

根据您所拥有的,FormWithWorker 必须(至少)作为您正在实例化的类型的基类,因此您可以这样做:

FormWithWorker formToLoad = (FormWithWorker)fi.GetValue(this);
if (formToLoad == null)
{
    formToLoad = (FormWithWorker)System.Activator.CreateInstance("MyAssemblyName", formType);
}

【讨论】:

  • 我试过这个,但由于System.Activator.CreateInstance 方法返回一个ObjectHandler,我收到了一个转换错误。我刚刚阅读了@Andras Vass 的想法,即调用Unwrap() 并将两者结合起来是我接下来要尝试的。
【解决方案4】:

虽然通用界面是解决此问题的一种方法,但界面并非适用于所有场景。上面的决定是使用工厂模式(switch 语句 - 具体类选择)或使用反射之一。有一个堆栈帖子可以解决这个问题。我相信您可以直接将此应用于您的问题:

Method Factory - case vs. reflection

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-06-09
    • 2023-02-10
    • 1970-01-01
    • 2018-04-11
    • 1970-01-01
    • 2023-02-08
    相关资源
    最近更新 更多