创建对象实例而不调用构造函数?


68

在C#中,有没有一种方法可以实例化一个类的实例而不调用其构造函数?

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

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


3
与此相关的注释,有些人往往没有意识到,如果在构造过程中引发异常,则您的对象仍然存在..如果它具有终结器,该终结器仍将运行,则需要谨慎对待未发生的情况在构造函数中。
meandmycode

你为什么要这个?如果只是出于好奇,那很好。
nawfal 2014年

@nawfal WCF将其用于序列化引擎,这是它通常很有用的原因之一。我的具体案例实际上是在玩一个现有的对象模型(SharePoint OM),以查看是否有可能为实验目的解决一些错误/限制。确实很hacky,但是还是实验性的。
克里斯吉伦

Answers:


90

我没有尝试过此方法,但是在反序列化过程中使用了一种称为FormatterServices.GetUninitializedObject的方法。

来自MSDN的评论说:

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


就我而言,它仅用于原型制作。不是为了任何人都将用于远距离重要的事情的真实代码。
克里斯吉伦

我正计划使用它从未支持的序列化格式反序列化DataContract对象。
Michael Shaw

6
我已经对此进行了测试,可以确认FormatterServices.GetUninitializedObject()不会避免调用静态ctor。:(
杰森·埃文斯

4
@JasonEvans我认为您永远都不能阻止静态ctor的运行,因为在加载类型时CLR会加载它们。您实际上根本无法在不加载类的类型的情况下完全引用它,所以我不确定您如何解决它
Orion Edwards

1
@nawfal反射并非在所有情况下都可行。我问这个问题的全部原因是因为我正在处理一个具有内部构造函数的类型,该内部构造函数需要本身就是复杂内部类型的参数。事实证明,使用反射很难生成其他相关类型,而又不使无尽反射的滑坡下降。
克里斯·吉伦


6

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


0

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


0

请参阅System.Activator.CreateInstance函数。


可悲的是,这是一个正确的答案。该问题在这篇文章之后被编辑。
nawfal 2014年

0

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

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

假设您的班级称为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);

丑陋,但是行得通。

当然,将构造函数标记为内部构造可能是完全合理的理由,因此在滥用该类并进行反思之前,您应该真正考虑所需的对象。


您是否不创建对象实例就调用了构造函数?(例如:与问题所在完全相反?)
Orion Edwards

我通过调用隐藏的构造函数来创建对象,这是他想要的,直到他编辑自己的帖子。
FlySwat

问题的修订历史与您不同。也许这太肮脏了,所以阿特伍德(Atwood)试图过度优化。我记得他在播客中提到的只是简单地丢弃了发布问题后几分钟内发生的任何更改记录
Orion Edwards

stackoverflow.com/revisions/296584/list <-我看到他添加了他的澄清说明。
FlySwat

0

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


0

如果该类(及其引用的对象的类)是可序列化的,则可以使用输出到MemoryStream的BinaryFormatter进行序列化(创建字节数组byte [])来进行深度复制,然后反序列化。有关将对象转换为字节数组的信息,请参见此问题的答案。(但请注意-保存字节数组以在以后/其他地方使用可能不起作用。IOW不会将字节数组保存为文件或其他持久形式。)


-1

您要执行的操作违反了开发托管编程的哲学。.Net Framework和C#的构建原则是:只要有可能,就应从其基础内存中抽象出对象。对象是对象,而不是结构化的字节数组。这就是为什么您不能将对象强制转换为空指针。从对象的底层内存中抽象出对象时,建议对象实例可以存在而无需调用构造函数是根本无效的。

也就是说,.Net框架对以下事实作出了让步:实际上,对象实际上是在内存中表示的。有了一些创造力,就可以实例化值类型而无需调用其初始化程序。但是,如果您认为需要这样做,则可能是做错了事。


-1

我注意到了该主题的“死气沉沉”,但只是为了进一步澄清读者的意见,并提出了一个最终的答案,而这个问题在发布问题时是不可能的。来了

似乎可以通过将值分配给其属性来实例化一个类而无需使用其构造函数。这是该类型的实例化在MSDN中的操作方法http://msdn.microsoft.com/zh-cn/library/bb397680.aspx的地址

似乎这是一种并不为人所知的技术,因为我在CodeProject中的一篇文章中遇到了它,然后在Google上搜索了它,却没有找到任何有关它的信息,后来访问了MSDN主页,并且有一篇帖子将我链接到了该确切的位置。学科。因此,似乎这是一个未知主题,而不是新主题,因为CodeProject帖子上的日期为2008年5月。

希望这对谷歌的帮助,并遇到这个问题的人。


2
错了 无参数构造函数仍将被调用。如果只有带有参数的构造函数,则不能编译也不会编译。MSDN具有误导性:不是因为您没有键入而“不打电话”。
Jabe

链接的MSDN文章对此非常清楚:“编译器通过首先访问默认实例构造函数,然后通过处理成员初始化来处理对象初始化程序。因此,如果默认构造函数在类中声明为私有,则需要使用对象初始化程序公众访问将失败。”
蒂姆·库尔特

-3

这里没有人充分阐明“无法完成”的含义。

构造函数创建对象的对象。您的问题类似于“如何在不像城堡的形状下建造沙堡?” 您不能-您将拥有的只是一堆沙子。

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

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

(注意:即使仅在MyStruct中有效,也是一种值类型)

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.