问题 将盒装对象转换回原始类型


我希望有两个答案中的一个,要么不可能,要么非常简单,我忽略了明显的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; }
}

很明显,这将无法编译,因为没有真正的方法可以作为变量进行转换。但是,我希望有一种方法可以获得对实际对象和类型的引用,或者至少是一种动态重新创建类型的方法。

我希望有一些简单的东西我可以忽略,或者更好的解决方法。关键是能够将任何对象包装在容器中,并稍后弄清楚它是什么。


1033
2018-06-10 01:30


起源

我的代码中没有看到任何装箱。你需要这样做 var actualType = boxed as Container<Containee>; 使其可编辑。 ` - Alex Aza
拳击课不会发生。只有值类型(int等)被装箱。你要找的是 casting。你有盒装到对象。 - Richard Schneider
我认为代码示例正在阻碍。他提到了一个事件处理程序,所以把它想象成接收 object sender。他想知道在编译时是否有任何方法可以恢复真正的类型并享受intellisense带来的好处。 - Anthony Pegram
@Alex:那不是拳击。 @Richard是对的; boxing正在创建一个引用的包装器,以便值类型可以存储在堆上。 - Adam Robinson
我认为你没有清楚地看到你。你说你不想“失去IntelliSense和一切”,但如果你在编写代码时不知道变量的类型,那么intellisense会给你什么?您不能将对象强制转换为未知类型,因为它没有实际效果。 - Adam Robinson


答案:


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

这很容易(你已经在做了)。

Type actualType = param.GetType();

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

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

这是事情有点落空的地方。 C#中的转换操作符(其用法是人们称之为“转换”)可以做两件事:

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

在你的情况下,第一个选择是正确的;与所有运算符一样,转换运算符不是多态的。也就是说,只有在运算符上定义了运算符时才应用它 类型 正在被引用,而不是 目的 那是被引用的。如果您想进一步澄清这一点,请告诉我,但我不认为这与您的问题密切相关,所以除非被问到,否则我不会进一步深入讨论。

第二个选项是唯一可以实际应用于您的选项,但请考虑您希望执行此操作的唯一两个原因:

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

(绝大多数演员是出于理由#1)

您希望使用这些选项中的任何一个的原因是,您可以拥有强类型对象并使用在该类型上定义的各种成员。但所有这些东西只适用于你的类型 知道 当你编写代码时。在编译时强制转换为未知的类型是没有意义的,因为强制转换对实际对象没有任何作用(它是,并且将保留,它的真实类型;唯一改变的是类型该 变量 通过它来引用对象)。

如果你可以提供一个进一步充实的例子来说明你实际上要做的事情(完成代码,你要么喜欢或期望它可以工作),我可能能够提供一些模仿你的东西更接近你的东西想要,但正如我所描述的那样,这是我能得到的具体。


8
2018-06-10 02:38





首先:那不是“拳击”。拳击是为价值类型,如 struct秒。

第二个:你可能需要的是:

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

第三点:您的示例代码确实如此 variable1 as variable2,这没有多大意义。 :\那你打算做什么?也许有更好的方法。


4
2018-06-10 01:35





var actualType = boxed as originalType;

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

var 是一个编译时构造。它与直接声明具有正确类型的变量相同。除了更容易键入外,它的主要用途是匿名类型,如暗示的那样,没有名称。

无论如何,为了解决你的问题,最好的办法是使用动态代码生成 Reflection.Emit 要么 CodeDom (如果你不了解ILASM,后者更容易理解,但速度要慢得多)。

根据你真正想做的事情,你可能会得到类似的东西

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

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


1
2018-06-10 01:40



虽然你关于为什么不可能将变量转换为变量的观点显然是正确的,但我认为帖子的后半部分(肉,换句话说)是不成熟的; OP并不清楚他想要什么 做,所以我不确定是否有人能够就如何做到这一点提出建议;) - Adam Robinson
@亚当, Depending on what you actually want to do 我把它包括在内是有原因的;) - Mike Caron


根本问题是我有一个   通过一个传递的通用对象   包装对象的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 ();
    
  • 在完成“魔法铸造”之后,重新解释您的问题并扩展您的样本,看看它是如何工作的。这会让我们知道你想要完成什么。


1
2018-06-10 02:05



这似乎与最初的意图最接近,IMO。 - O'Rooney


你可以用 dynamic

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

这应该编译和工作。


0
2018-06-10 02:23