【问题标题】:cast with a Type variable使用类型变量强制转换
【发布时间】:2014-02-16 05:21:35
【问题描述】:

下面的代码不起作用,我想知道如何将实例动态转换为在运行时确定的类型?

Convert.ChangeType() 返回一个仍然需要转换的对象。所有尝试 Invoke() GetConstructor() 或 Activator.CreateInstance() 的尝试也是如此,见下文。在某些时候我需要显式地输入代码,我希望避免它或尽可能地推出它。

Type type = Type.GetType ("RandomChildClass");
Object obj = Activator.CreateInstance (type, new Object[]{ "argument" });
var instance = (type)obj;

我知道我可以创建一个方法来接受 ,但我仍然有同样的问题,不知道如何用动态随机类型调用它 Casting a variable using a Type variable

【问题讨论】:

  • 投完后你想用instance做什么?
  • 只是将它添加到基本类型的列表中

标签: c# casting activator


【解决方案1】:

不可能使用Type来确定表达式的类型。 (泛型类型参数不同于值,因为它们被编码到类型系统中。)

变量的值来自run-time代码执行,而表达式类型是compile-time构造。不用说,编译发生在代码运行之前,因此使用变量进行强制转换是不可能的。

Reflection (albiet unwieldy) 或 dynamic (这基本上是更易于使用的反射) 允许调用任意方法或访问通用对象类型表达式的属性/字段 - 这有时被称为“后期绑定”。但是,调用操作的表达式的类型仍然是对象。

Interfaces 可用于统一不同的类实现以实现正确的静态类型。然后可以将新创建​​的对象强制转换为需要的适用接口。就像其他表达式一样,类型是 compile-time 构造(因此必须直接指定接口),但代码现在不受特定类的影响。

如果创建一个系统以使这些“动态类”直接在静态类型 (C#) 代码中使用,并且接口可以得到保证或被限制在一个小集合中,那么使用接口可能是最简洁的方法:例如var myAction = (IMyAction)obj。否则,回退到动态访问 - 直接或在门面后面。

【讨论】:

  • 我希望动态填写演员阵容的“(IMYACTION)”部分,但从您的解释来看,这根本不可能。对吗?
  • @user2994682 没错。出于同样的原因,这是不可能的。
  • @user2994682 你可能会发现DuckTyping 库 (as discussed here) 很有趣。
【解决方案2】:

将你的变量定义为dynamic,它应该可以工作,我的意思是你仍然不能转换它,但你可以访问你的方法:

dynamic obj = Activator.CreateInstance (type, new Object[]{ "argument" });

例如:

【讨论】:

  • 是否可以不使用动态?我收到编译错误
【解决方案3】:

根据您的评论,您正尝试将其添加到List<SomeDynamicClass>。我的第一个想法是,如果您不能静态访问SomeDynamicClass,那么我很好奇您是如何或为什么拥有List<SomeDynamicClass>。听起来使用List<object>List<dynamic> 代码会更好。

如果您受限于List<SomeDynamicClass>,那么听起来您应该直接跳到使用反射来调用Add 方法

var listType = theList.GetType();
var method = listType.GetMethod("Add");
method.Invoke(theList, obj);

【讨论】:

  • 我很抱歉造成混乱,我真的只是想要一种动态转换到特定子类的方法。之后,在基础列表上进行简单的添加就可以了。
  • 如果我构造一个子对象(通过调用构造函数)并将其转换为父对象,稍后当我想使用它时,我仍然需要将它显式地转换为子对象。但是,如果我能够保留它的类型,将其添加到基类列表中,在使用时它仍然保留自己的类型(而不是基类),我的假设是否错了?
【解决方案4】:

如果您静态知道基类的类型,请强制转换为该类型。例如:

void AddToList(List<Control> list, string typeName) {
    var control = (Control)Activator.CreateInstance(typeName);
    list.Add(control);
}

然后

var list = new List<Control>();
AddToList(list, "Button"):
AddToList(list, "Label"):
AddToList(list, "ListBox"):

如果你以后需要做一些特定于类型的事情,你可以测试类型并强制转换:

foreach (Control child in list)
    DoSomethingWithThe(control);

void DoSomethingWithThe(Control control)
{
    Button button = control as Button;
    if (button != null)
    {
        button.Click += Button_Click;
        return;
    }

    Label label = control as Label;
    if (label != null)
    {
        label.MouseOver += Label_MouseOver;
        return;
    }
    //... etc.
}

或者使用OfType方法:

foreach (Button button in list.OfType<Button>())
    button.Click += Button_Click;

foreach (Label label in list.OfType<Label>())
    label.MouseOver += Label_MouseOver;

您也可以使用反射,但是这相当麻烦,所以我不会举例。如果您有一个在基本类型的不同子类型上定义但不在基本类型上的同名属性,则反射将很有用。如果有很多子类型,类型检查会很麻烦,所以反射可能是更好的方法。

【讨论】:

  • 谢谢,我想尽量保持孩子的类型。有可能吗?
  • @user2994682 正如 user2864740 回答的那样,不,不可能在编译时保留子项的静态类型。但是,子对象始终保留其运行时。例如,此代码生成一个包含一个字符串和一个装箱 int 的列表:var objects = new List&lt;object&gt; { "One", 2 }。这些对象将始终是一个字符串和一个装箱的 int。如果您需要在从列表中检索子项后对子项执行特定类型的操作,请测试类型并强制转换(可能使用 Linq OfType 方法),或者使用反射。我将添加几个示例。
猜你喜欢
  • 2012-06-20
  • 1970-01-01
  • 2021-01-24
  • 2015-08-17
  • 1970-01-01
  • 2012-02-18
  • 2021-05-17
  • 2021-03-13
  • 1970-01-01
相关资源
最近更新 更多