C# 我想用C在堆栈上分配一个对象#

C# 我想用C在堆栈上分配一个对象#,c#,memory-management,C#,Memory Management,假设我有这个C#类: 如何在函数中声明它的对象,该对象将在堆栈上而不是堆上分配? 换言之,我想避免在这个问题上使用“new”关键字。此代码不正确: HttpContextEx ctx = new HttpContextEx(); // << allocates on the heap! HttpContextEx ctx=new-HttpContextEx();//

假设我有这个C#类:

如何在函数中声明它的对象,该对象将在堆栈上而不是堆上分配?
换言之,我想避免在这个问题上使用“new”关键字。此代码不正确:

HttpContextEx ctx = new HttpContextEx(); // << allocates on the heap!

HttpContextEx ctx=new-HttpContextEx();//<你必须来自C++。在.Net中,情况并非如此

所有引用类型都在托管堆上分配,GC在其中跟踪它们。对于由快速退出的函数定义范围的对象引用,分配的对象很可能只保留在托管堆的第0代中,这将导致非常高效的内存收集。托管堆经过调优以处理这样的短期对象。它甚至没有与你可以使用的C++堆相同的分配策略。
这就是CLR的工作原理。如果您想以另一种方式工作,请尝试非托管运行时。

您不能(也不应该)这样做。即使要使用
struct
(将放在堆栈上),也必须对包含的类使用
new
操作符。严肃地说,如果您切换到另一种语言,也要切换您的态度。

如果您使用
struct
将其更改为值类型,并在方法体中创建一个新实例,它将在堆栈上创建它。但是,成员(因为它们是引用类型)仍将位于堆上。语言(无论是值还是引用类型)仍然需要新的运算符,但您可以使用
var
来避免重复使用类型名称

var ctx = new HttpContextEx(); 

否则,就以C为例,因为GC做得很好。

在.NET中,类是引用类型,结构是值类型

如果您确实希望在堆栈上分配类型,可以创建一个结构。但是,有几个理由不这样做:

  • 结构更复杂,无法正确实现。在有充分的理由创建结构之前,您应该坚持使用类
  • 结构用于表示单个单元的类型,而您的类型只是三个独立单元的容器
  • 结构不应大于16字节才能有效工作。在32位系统上,你会受到这个限制,但在64位系统上,结构会比这个大
  • 结构应该是不可变的,才能作为值类型正常工作,而您的类型不是
最后,在堆上分配小对象本身并没有什么坏处。内存管理器实际上是专门设计来高效地处理小型短期对象的

下面是一个代码示例,如果它是一个结构,则不起作用,但如果它是一个类,则效果很好:

public void SetContext(HttpContextEx ex) {
  ex.context = HttpContext.Current;
  ex.req = ex.context.Request;
  ex.res = es.context.Response;
}

HttpContextEx ctx = new HttpCoontextEx();
SetContext(ctx);

使用ValueType派生来获得堆栈分配只是CLR当前实现的一个产物。在规范中没有这样的要求。@Poni:虽然结构可能有用,但它们应该是不可变的,并且很小(例如16字节或更少),因为每次将它们传递给子例程时,代表对象的整个内存块都会被复制,除非您想在所有函数参数中使用
ref
。在5年以上的C#开发中,我还没有找到一个需要担心.Net堆栈/堆速度的案例。简言之,我只是声明类,让.Net担心它,而我专注于应用程序。如果这让你的船漂浮起来,好吧-我只是希望(对你来说)它也能让你雇主的船漂浮起来。哇,Poni。您是否愿意接受性能打击以避免在C#中使用最佳实践?你知道,在堆栈上分配甚至不是你的习惯,而是编译器或运行时的习惯。您只需声明一个变量,编译器/运行时就可以决定将其放置在何处。所以你的意思是。。。。你想让.NET运行时来接受你的C++编译器的习惯吗?@ Poni:很遗憾,你最近的三条评论表明C++给你的心灵造成了不可挽回的破坏和对软件的抽象思考能力。你对一堆和一堆的定义让我的大脑爆炸。你对底层实现的假设太多了,你必须用C#思考too@Poni那么,你应该只写C语言而不是试图在C++中写C++。我想我不太明白问题出在哪里,但C#/.NET的设计是有原因的。创建
HttpContextEx
到底有什么意义?现有的
HttpContext
类已经提供了一个
Request
Response
属性。@John Rasch我很懒,当我在12个以上的函数之间传递同样的六个指针时,我可以简单地传递一个漂亮的对象。简单来说,我没有办法知道会出现这样的答案,如果我知道这些是我不会问的答案,我会吗?!?另外,aqwert的答案是答案,没有所有的“bla-bla”。“但是您可以使用var来消除类型名称的双重使用”,这样我就可以链接它-将值类型放在堆栈上是所有当前运行时的一个实现细节,但它们也可能存储在堆上。()关于守则;我明白。我也明白,通过输入'ref'关键字,我已经准备好了。正确吗?@Poni:您可以使用ref关键字使代码工作,但也有其他类似情况,未正确实现的结构无法工作。
public void SetContext(HttpContextEx ex) {
  ex.context = HttpContext.Current;
  ex.req = ex.context.Request;
  ex.res = es.context.Response;
}

HttpContextEx ctx = new HttpCoontextEx();
SetContext(ctx);