【问题标题】:Cast Boxed Object back to Original Type将盒装对象转换回原始类型
【发布时间】:2011-06-10 10:51:28
【问题描述】:

我希望有两个答案之一,要么是不可能的,要么是极其简单的,我忽略了明显的 Google 查询。

根本问题是我有一个通过EventHandler 传入的通用对象,该对象将对象装箱并混淆了真实类型;只有在运行时我才知道对象是什么。

诚然,dynamic 关键字可以解决这个问题,但如果可以避免的话,我希望不要丢失 IntelliSense 和一切。另外,如果不进行大量反射,它并不能解决不知道通用对象的每个属性是什么的问题。

编辑:想法是能够在方法参数中确定对象的真实类型,然后将该对象转换为真实类型,而无需事先知道它。这只是一个简化的例子。盒装可能是错误的术语。

一个例子:

public class Program
{
    static void Main(string[] args)
    {
        var container = new Container<Containee>(
            new Containee
            {
                Property1 = Guid.NewGuid(),
                Property2 = "I'm a property!",
                Property3 = DateTime.Now
            }
        );

        var boxed = (object)container;

        var originalType = boxed.GetType();

        // DOES NOT COMPILE: would like an operation like this
        // EDIT: Request for more detail
        var actualType = boxed as originalType;
        actualType.Entity.Property2 = "But I like this better.";
    }
}

public class Containee
{
    public Guid Property1 { get; set; } 
    public string Property2 { get; set; }
    public DateTime Property3 { get; set; }
}

public class Container<T>
{
    public Container(T entity)
    {
        Entity = entity;
    }

    public T Entity { get; internal set; }
}

显然这不会编译,因为实际上并没有办法将其转换为变量。但是,我希望有一种方法可以获取对实际对象和类型的引用,或者至少是一种动态重新创建类型的方法。

我希望我忽略了一些简单的事情,或者是一种更好的方法来解决它。关键是能够将任何对象包装在容器中,然后弄清楚它是什么。

【问题讨论】:

  • 我在您的代码中看不到任何装箱。您需要执行 var actualType = boxed as Container&lt;Containee&gt;; 以使其可编译。 `
  • 类的装箱不会发生。只有值类型(int 等)被装箱。您要查找的词是casting。您已将 boxed 投射到对象。
  • 我认为代码示例会妨碍您。他提到了一个事件处理程序,因此可以将其视为接收object sender。他想知道在编译时是否有任何方法可以取回真实类型并享受智能感知的好处。
  • @Alex:那不是拳击。 @Richard 是正确的;装箱正在创建一个引用的包装器,以便可以将值类型存储在堆上。
  • 我认为你的意思不是很清楚。您说您不想“失去 IntelliSense 和一切”,但是如果您在编写代码时不知道变量的类型,那么 IntelliSense 会给您什么?您不能将对象强制转换为未知类型,因为它没有实际效果。

标签: c# generics reflection event-handling boxing


【解决方案1】:

想法是能够在方法参数中确定对象的真实类型

这很容易(而且您已经在这样做了)。

Type actualType = param.GetType();

这将为您提供对象的实际具体类型

然后将该对象转换为真实类型

这就是事情有点偏离轨道的地方。 C# 中的强制转换运算符(人们称之为“强制转换”)可以做两件事:

  1. 使用特定于类型的显式转换,通过将转换应用于现有对象来创建新对象(请注意,这是一个创建的引用;原始对象的类型永远不会改变)
  2. 允许开发人员将对象引用为在其继承层次结构中与当前提供的不同级别的类型(或在层次结构中低于当前引用的类型上实现的接口)李>

在您的情况下,第一个选项是正确的;与所有运算符一样,强制转换运算符不是多态的。也就是说,只有在被引用的 type 而不是被引用的 object 上定义的运算符才会被应用。如果您想对此进一步澄清,请告诉我,但我认为这与您的问题无关,因此除非被问到,否则我不会进一步讨论。

第二个选项是唯一可以实际适用于您的选项,但请考虑您想要这样做的唯一两个原因:

  1. 这样您就可以将对象称为特定的具体类型,该类型的级别低于当前提供的级别(在您的情况下,您的对象是object,所以它几乎是最高级别)
  2. 这样您就可以将对象称为层次结构中更高的类型,从而可以绕过隐藏(但不被覆盖)的成员。

(绝大多数演员都是出于原因#1)

您想要使用其中任何一个选项的原因是您可以拥有一个强类型对象并使用在该类型上定义的各种成员。但所有这些都只适用于您在编写代码时知道的类型。转换为在编译时未知的类型是没有意义的,因为转换不会对实际对象做任何事情(它是,并且应该保持它的真实类型;唯一改变的是引用对象的变量)。

如果您能提供一个进一步充实的示例来说明您实际尝试做的事情(根据您的需要或期望它完成代码),我也许可以提供一些更接近建模的东西你想要什么,但正如它所描述的那样,这是我能得到的最具体的。

【讨论】:

    【解决方案2】:

    首先:那不是“拳击”。装箱适用于值类型,例如 structs。

    其次:您可能需要的是:

    • C# 没有的编译时反射
    • 动态代码生成,您可以(痛苦地)使用Reflection.Emit

    第三点:您的示例代码执行variable1 as variable2,这实际上没有意义。 :\ 在那之后你打算做什么?也许有更好的方法。

    【讨论】:

      【解决方案3】:
      var actualType = boxed as originalType;
      

      我们在同一页面上,让我解释一下为什么这是不可能的。

      var 是一个编译时构造。这与直接声明具有正确类型的变量相同。除了更易于键入之外,它的主要用途是用于匿名类型,隐含地,它们没有名称。

      无论如何,要深入了解您的问题,最好的办法是使用动态代码生成,使用 Reflection.EmitCodeDom(如果您不了解 ILASM,后者更容易理解,但是慢得多)。

      根据您实际想要做的事情,您可能能够摆脱类似的事情

      if(someObject is Container<Containee>) {
           var container = (Container<Containee>)someObject;
           //...
      }
      

      但是,如果你可以期待任何类型,那么……祝你好运。

      【讨论】:

      • 虽然您关于为什么不可能将变量转换为变量的观点显然是正确的,但我认为帖子的后半部分(换句话说,肉)还为时过早。 OP 不清楚他想做什么,所以我不确定是否有人能够提供有关如何做到这一点的建议;)
      • @Adam, Depending on what you actually want to do 我把它包括在内是有原因的 ;)
      【解决方案4】:

      根本问题是我有一个 通用对象通过 EventHandler 将对象和 混淆真实类型;仅在 运行时我知道对象是什么吗?

      如果类型只在运行时已知,你想如何处理它?您不能调用任何特定的类方法,因为无论如何您都不知道确切的类型,除非所有对象共享一些可以提取为接口的方法集。

      基本上,您有几种选择:

      • 使用is,为不同的类型做不同的事情:

        object value = GetValue ();
        if (value is Program)
            ((Program)value).Run ();
        else if (value is Animal)
            ((Animal)value).Run ();
        
      • 如果所有可能的类型都应该共享一组操作,请使用接口:

        object value = GetValue ();
        IRunnable runnable = (IRunnable)value;
        runnable.Run ();
        
      • 重新表述您的问题,并根据您在完成“神奇铸造”后的工作方式扩展您的示例。这会让我们知道您想要完成什么。

      【讨论】:

      • 这似乎最接近最初的意图,IMO。
      【解决方案5】:

      你可以使用dynamic:

      dynamic actualType = boxed;
      actualType.Entity.Property2 = "But I like this better.";
      

      这应该可以编译并工作。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2013-01-12
        • 2015-12-19
        • 1970-01-01
        • 1970-01-01
        • 2023-03-13
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多