【问题标题】:Create object instance without invoking constructor?创建对象实例而不调用构造函数?
【发布时间】:2008-11-17 19:24:55
【问题描述】:

在 C# 中,有没有办法在不调用构造函数的情况下实例化类的实例?

假设该类是公共的并且在第 3 方库中定义并且构造函数是内部的。我想这样做的原因很复杂,但知道是否可以使用某种 C#hackery 会很有帮助。

注意:我特别不想调用任何构造函数,因此不能使用反射访问内部构造函数。

【问题讨论】:

  • 与此相关且有些人往往没有意识到的是,如果您在构造过程中抛出异常,您的对象仍然存在。如果它有一个仍然会运行的终结器,那么您需要小心不要对构造函数中发生的事情做出任何假设。
  • 你为什么想要这个?如果只是好奇,那也没关系。
  • @nawfal WCF 使用它作为它的序列化引擎,这是它通常有用的原因之一。我的具体案例实际上是在使用现有的对象模型(SharePoint OM)来查看是否可以解决一些错误/限制以用于实验目的。确实很老套,但又是实验性的。

标签: c#


【解决方案1】:

这个我没试过,但是反序列化的时候有一个叫FormatterServices.GetUninitializedObject的方法。

来自 MSDN 的评论说:

因为对象的新实例 被初始化为零并且没有 构造函数运行时,对象可能 不代表被视为的状态 由该对象有效。

【讨论】:

  • 就我而言,它仅用于原型设计。不适用于任何人都会将其用于任何远程重要的真实代码。
  • 我正计划使用它从不受支持的序列化格式反序列化 DataContract 对象。
  • 我已经对此进行了测试,可以确认 FormatterServices.GetUninitializedObject() 不会避免调用静态 ctor。 :(
  • @JasonEvans 我认为您永远无法阻止静态 ctor 的运行,因为 CLR 在加载 type 时会加载它们。如果不加载它的类型,你实际上根本无法引用一个类,所以我不太确定你会如何绕过它
  • @nawfal 反射并非在所有情况下都实用。我问这个问题的全部原因是因为我正在处理一个类型,它有一个内部构造函数,需要参数,这些参数本身就是复杂的内部类型。事实证明,使用反射太难生成这些其他相关类型,而不会掉下无尽反射的滑坡。
【解决方案2】:

实际上,听起来他们将构造函数设为内部,因此您无法实例化它。它可能有一个构建器或工厂方法。

看看这些文章:

Preventing Third Party Derivation: Part 1

Preventing Third Party Derivation: Part 2

他们有点解释推理。

【讨论】:

    【解决方案3】:

    与许多人的看法相反,构造函数与对象的实例化根本没有太大关系(相当误导的术语)。构造函数是一种特殊方法,可以在对象实例化后调用,以允许该对象正确初始化自身。在 C++ 中,对象实例化为对象分配内存,在 .NET 和 Java 中,它根据字段的类型(0、null、false 等)被分配并预初始化为默认值。然后运行时调用构造函数。新的操作符将这两个单独的操作封装成一个看似单一的操作。 如果不使用构造函数就无法创建实例,那么反序列化在 .NET 中永远不会起作用。也就是说,所谓的 ConstructorInfo 类型在调用其 Invoke(...) 方法时既充当 new 运算符又充当构造函数。

    【讨论】:

      【解决方案4】:

      可能可以通过反射访问构造函数并像那样调用它(但我不确定它是否会工作,因为构造函数是内部的 - 你必须对其进行测试)。 否则,据我所知,如果不调用构造函数,您将无法创建对象。

      【讨论】:

        【解决方案5】:

        参见 System.Activator.CreateInstance 函数。

        【讨论】:

        • 遗憾的是,这是一个正确的答案。这个问题是在这篇文章之后编辑的。
        【解决方案6】:

        编辑:你更新了你的问题,你想构造一个没有构造函数的类。或者调用一个默认的“Empty Constructor”。

        这是无法做到的,因为如果已经指定了默认构造函数,编译器将不会生成默认构造函数。但是,为了读者的利益,这里是如何获取内部、受保护或私有构造函数:

        假设你的班级叫 Foo:

        using System.Reflection;
        
        // If the constructor takes arguments, otherwise pass these as null
        Type[] pTypes = new Type[1];
        pTypes[0] = typeof(object);    
        object[] argList = new object[1];
        argList[0] = constructorArgs;
        
        ConstructorInfo c = typeof(Foo).GetConstructor
            (BindingFlags.NonPublic |
             BindingFlags.Instance,
             null,
             pTypes,
             null);
        
        Foo foo = 
            (Foo) c.Invoke(BindingFlags.NonPublic,
                           null, 
                           argList, 
                           Application.CurrentCulture);
        

        丑陋,但有效。

        当然,将构造函数标记为内部构造函数可能有完全正当的理由,因此在通过反射来滥用该类之前,您应该真正考虑一下您想要什么的逻辑。

        【讨论】:

        • 你不是直接调用了构造函数,没有创建对象实例吗? (例如:与问题所问的完全相反?)
        • 我通过调用隐藏的构造函数创建了对象,这是他在编辑帖子之前想要的。
        • 问题的修订历史与您不一致。也许这是肮脏的阿特伍德试图过多地优化事物。我记得在一个播客中,他提到简单地丢弃问题发布后几分钟内发生的任何更改记录
        【解决方案7】:

        您必须调用构造函数来创建对象。如果没有你喜欢的,也许你可以使用像 Mono 项目的 Cecil 这样的字节码重写库。它适用于 Windows 和 Linux。从我看到的一些演示中,它看起来很酷。您可以更改方法的保护级别和各种疯狂的东西。

        【讨论】:

          【解决方案8】:

          如果类(及其引用的对象的类)是可序列化的,则可以通过使用输出到 MemoryStream 的 BinaryFormatter 序列化(创建字节数组 byte[])然后反序列化来创建深层副本。见the answers to this question on converting an object to a byte array。 (但请注意 - 保存字节数组以供以后/其他地方使用可能不起作用。IOW 不会将字节数组保存到文件或其他持久形式。)

          【讨论】:

            【解决方案9】:

            您要求做的事情违反了开发托管编程的理念。 .Net Framework 和 C# 的构建原则是尽可能将对象从其底层内存中抽象出来。对象是对象,而不是结构化的字节数组。这就是为什么您不能将对象强制转换为 void 指针的原因。当对象从其底层内存中抽象出来时,建议对象实例可以在没有调用构造函数的情况下存在从根本上是无效的。

            也就是说,.Net 框架已经做出让步,事实上,对象实际上是在内存中表示的。有了一些创造力,就可以在不调用它们的初始化程序的情况下实例化值类型。但是,如果您觉得需要这样做,那么您可能做错了。

            【讨论】:

              【解决方案10】:

              我注意到该主题的“死气沉沉”,但只是为了向更多读者澄清,并给出发布问题时可能不可能的最终答案。就这样吧。

              您似乎可以通过为其属性分配值来实例化一个类而不使用它的构造函数。这是 MSDN 中这种类型的实例化的操作方法的地址http://msdn.microsoft.com/en-us/library/bb397680.aspx

              这似乎是一种不为人所知的技术,因为我在 CodeProject 的一篇文章中遇到了它,然后用谷歌搜索它并没有找到任何关于它的信息,后来访问了 MSDN 主页,有一个帖子链接了我到那个确切的主题。因此,它似乎是一个未知主题,而不是一个新主题,因为 CodeProject 发布日期为 2008 年 5 月。

              希望这可以帮助谷歌这个并遇到这个问题的其他人。

              【讨论】:

              • 错了。仍将调用无参数构造函数。如果只有带参数的构造函数,则 this 不能也不会编译。 MSDN 具有误导性:不是因为您不键入就“不调用”。
              • 链接的MSDN文章对此非常清楚:“编译器通过首先访问默认实例构造函数,然后通过处理成员初始化来处理对象初始化程序。因此,如果默认构造函数被声明为私有在类中,需要公共访问的对象初始化器将失败。”
              【解决方案11】:

              这里没有人完全阐明“无法做到”的含义。

              构造函数创建对象的东西。你的问题类似于“我怎样才能建造一座沙堡而不把它塑造成一座城堡?”你不能——你只有一堆沙子。

              您可以做的最接近的事情是分配一块与您的对象大小相同的内存:

              byte[] fakeObject = new byte[sizeof(myStruct)];
              

              (注意:即使只在 MyStruct 中有效也是一个值类型)

              【讨论】:

                猜你喜欢
                • 2013-09-24
                • 1970-01-01
                • 1970-01-01
                • 2018-07-06
                • 1970-01-01
                • 2023-04-03
                • 2015-10-08
                • 2019-07-25
                相关资源
                最近更新 更多