关于C#中引用对象生存期的疑惑,求正解

x_craft77 2011-09-07 04:08:47
代码如下:
public class RepData
{
//定义
public MemoryStream memStream = null;
}
public RepData TestA()
{
//创建了
RepData repData = new RepData();
MemoryStream memStream = new MemoryStream();

//写入
memStream.WriteByte(65);
memStream.Seek(0, SeekOrigin.Begin);

//赋值
repData.memStream = memStream;

//释放-关键
memStream.Dispose();

//返回
return repData;
}

public void TestB()
{
//调用
RepData repData = TestA();

//输出
Response.BinaryWrite(repData.memStream.ToArray()); //正常运行,输出A
Response.Write((char)repData.memStream.ReadByte()); //运行出错,提示:无法访问已关闭的流
}

1、首先TestA在内部定义并实例化了一个RepData对象,并返回它,TestB调用TestA并接收返回值,按理说Test内部定义的RepData对象已经过了生存期了,为什么TestB中还能使用呢?

2、TestA中已经把memStream给Dispose了,为什么TestB中还能引用呢
Response.BinaryWrite(repData.memStream.ToArray()); //正常运行,输出A
Response.Write((char)repData.memStream.ReadByte()); //运行出错,提示:无法访问已关闭的流
虽然第二行代码被提示无法访问已关闭的流,但通过ToArray()确实是得到了正确的结果,问题在哪呢,是原来那个memStream还没有被释放?还不到时候?内存没有被写入新值,所以可以得出结果?.........
...全文
109 11 打赏 收藏 转发到动态 举报
AI 作业
写回复
用AI写文章
11 条回复
切换为时间正序
请发表友善的回复…
发表回复
x_craft77 2011-09-07
  • 打赏
  • 举报
回复
[Quote=引用 6 楼 sp1234 的回复:]
引用 2 楼 x_craft77 的回复:
//释放-关键
memStream.Dispose();


lz,这有什么可标注上“关键”的啊?你看过MemoryStream的源代码,你知道它在执行Dispose的时候执行什么功能吗?这就标记上“关键”我觉得非常地“无厘头”。

请lz给我一个解释,为什么要在这里写上这个“关键”二字?出处在哪里?
[/Quote]

我只是想说明,我这里释放了这个对象,说白了销毁了它
x_craft77 2011-09-07
  • 打赏
  • 举报
回复
[Quote=引用 4 楼 sp1234 的回复:]
就变量引用而言,htmlCode过了,可是 repData.htmlCode 没有过。

就string而言,它的赋值是复制string的机制。所以一个hello the world!被销毁了,另外一个hello the world!则被repData引用着。
[/Quote]

在C#中string是不被复制的,a = "123"; b=a; a和b指向的是同一个地址,当b中的字串被改变,比如b.replace时,才会copy一份吧
x_craft77 2011-09-07
  • 打赏
  • 举报
回复
[Quote=引用 3 楼 skyparty 的回复:]
问题1:
楼主要弄清一点,栈中变量的生命期,和堆中变量的生命期是不同的
RepData repData = new RepData();
//这里是在堆中创建了一个RepData对象,并把对象的引用传递给局部变量repData
return repData;
//这里把局部变量repData的一个值拷贝返回给函数调用处
//RepData repData = TestA();
//……
[/Quote]

说的有道理,但是堆中数据的生命期由什么决定呢,是由回收器自动调用吗?可以人工干预吗?
真相重于对错 2011-09-07
  • 打赏
  • 举报
回复
  • 打赏
  • 举报
回复
如果你说“我觉得有dispose的对象我都忍不住调用一下”,这个我完全可以理解你。实际上我也经常把任何一种Stream都写在using(){...}结构中。

如果你说“我从c的书上学到了坏毛病,没有手动调用点什么清理代码就害怕”这我也可以理解。

对于所有的Stream来说,dispose方法确实会调用
this.Close();

但是,对于MemoryStream来说,其Close方法什么都不做,因为根本不用做任何动作。我自己是第一种,觉得这个地方少一事不如多一事,所以我大多都会调用Strem对象实例的dispose。

问题是如果你把它写上注释“关键”两个字的时候,就不是很坦然地只是说“我就是少一事不如多一事,这里只是习惯性动作”了,你一定要解释清楚你为什么要特意加上“关键”这个具有威胁性的提示信息!
  • 打赏
  • 举报
回复
[Quote=引用 2 楼 x_craft77 的回复:]
//释放-关键
memStream.Dispose();
[/Quote]

lz,这有什么可标注上“关键”的啊?你看过MemoryStream的源代码,你知道它在执行Dispose的时候执行什么功能吗?这就标记上“关键”我觉得非常地“无厘头”。

请lz给我一个解释,为什么要在这里写上这个“关键”二字?出处在哪里?
chp845 2011-09-07
  • 打赏
  • 举报
回复
[Quote=引用 3 楼 skyparty 的回复:]

问题1:
楼主要弄清一点,栈中变量的生命期,和堆中变量的生命期是不同的
RepData repData = new RepData();
//这里是在堆中创建了一个RepData对象,并把对象的引用传递给局部变量repData
return repData;
//这里把局部变量repData的一个值拷贝返回给函数调用处
//RepData repData = TestA();
/……
[/Quote]
ls正解++
  • 打赏
  • 举报
回复
就变量引用而言,htmlCode过了,可是 repData.htmlCode 没有过。

就string而言,它的赋值是复制string的机制。所以一个hello the world!被销毁了,另外一个hello the world!则被repData引用着。
小赖赖 2011-09-07
  • 打赏
  • 举报
回复
问题1:
楼主要弄清一点,栈中变量的生命期,和堆中变量的生命期是不同的
RepData repData = new RepData();
//这里是在堆中创建了一个RepData对象,并把对象的引用传递给局部变量repData
return repData;
//这里把局部变量repData的一个值拷贝返回给函数调用处
//RepData repData = TestA();
//到这里,TestA()的局部变量repData生命期结束,但是repData的值被传递给TestB()中的repData
//TestB()中的repData继续持有对象的引用

问题2.
Dispose方法只是告诉运行期,这个对象可以被释放了,至于什么时候释放,并不一定,要等到下一次gc()
释放的也并不完全memStream还持有对象的引用
memStream=null;
Dispose也不建议手动调用,应该是由运行期销毁对象时自动调用更好
x_craft77 2011-09-07
  • 打赏
  • 举报
回复
改进后的代码如下:

public class RepData
{
//定义
public MemoryStream memStream = null;
public string htmlCode = string.Empty;
}
public RepData TestA()
{
//创建了
RepData repData = new RepData();
MemoryStream memStream = new MemoryStream();
string htmlCode = string.Empty;

//写入
memStream.WriteByte(65);
memStream.Seek(0, SeekOrigin.Begin);
htmlCode = "hello the world!";

//赋值
repData.memStream = memStream;
repData.htmlCode = htmlCode;

//释放-关键
memStream.Dispose();

//返回
return repData;
}

public void TestB()
{
//调用
RepData repData = TestA();

//输出
Response.BinaryWrite(repData.memStream.ToArray()); //正常运行,输出A
Response.Write(repData.htmlCode);
//Response.Write((char)repData.memStream.ReadByte()); //运行出错,提示:无法访问已关闭的流
}

添加了一个string htmlCode,在TestB中它被正常输出了,可TestA中定义的string不是过了生存期了吗,为什么TestB中还能引用????????
x_craft77 2011-09-07
  • 打赏
  • 举报
回复
如果把MemoryStream对象换成string呢,又会是什么结果

111,093

社区成员

发帖
与我相关
我的任务
社区描述
.NET技术 C#
社区管理员
  • C#
  • AIGC Browser
  • by_封爱
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

让您成为最强悍的C#开发者

试试用AI创作助手写篇文章吧