解决方案 »
- DataGridView显示中,如何做到:某一列的值为空时,另一列是不可编辑的,否则可编辑
- C# 一个容器里的控件如何移动到另一个容器里,并能在另一个容器里自动改变大小
- 验证护照, 现在的护照格式是怎样的?
- 如何外部控制ppt
- 兄弟们给我推荐几份C#面试题吧!!!
- excel设置格式问题
- 如何让Lable成一定的角度加载到WinForm窗口?
- 请教关于c#.net 服务器与客户端通过TcpClient相互通迅的问题
- 如何自定义treeView 的滚动条
- 水晶报表中,添加一个系统自带的World Sales Report.rpt,报crpe32.dll失败
- 【c#开源下载】微风IM 增加局域网P2P通信 V2
- 怎么才能用=赋值时进行克隆而不是引用?
public void Dispose() {
Dispose(true);
GC.SuppressFinalize(this);
}
所以你再在析构函数中执行的时候CONN已经为空了,C#中一般不提倡写析构
[A]:你的答案不靠谱。我的代码没问题,在程序中的构造函数中已经完成对conn构建,而且自此以后从没置空过。wjq:既然用C#就好好看看C#的垃圾回收/资源释放的方式和原理。用着C的概念来写C#程序还大叫这是C#的缺陷。你也真是个人才。
[A]:如果C#有其特殊的垃圾回收/资源释放的方式和原理,那至少也应该遵循标准的面向对象的操作方法。微软自建的一套标准是进步了? 还是在技术上比C++,Java次一等?starfd: 难道java,C++中释放前不需要判断要释放的对象是否存在??
[A]:是的, C++,Java在析构函数被调用的时候,类成员变量是不会被系统销毁释放的。既然C++,Java已经建立了一套面向对象的操作标准,为什么微软要自搞一套超复杂的操作标准困惑程序员?liucqa: 代码 写错了
[A]:你的回答严重不靠谱xu56180825: .NET代码中会自动释放对象,代码如下...,这个是从组件基类 Component 中继承的,所以你再在析构函数中执行的时候CONN已经为空了,C#中一般不提倡写析构。
[A]:您的回答是所有回复中最靠谱的。但您的回复也不能解决我的问题,因为conn是数据库连接对象,必须要做conn.Close(), 而不是简单的Dispose()和置空。否则会发生数据库连接用尽,系统崩溃的恶果。liucqa: 析构函数一般在C#调用了非托管资源之后才需要自己写,一般情况下是不需要自己写的
[A]:请问在程序中的conn是托管资源,还是非托管资源? 如果不写析构函数,系统会智能地知道conn是数据库连接吗?并且自动做conn.Close()操作吗?
你说你熟悉一些 c++ 这我相信,说你熟悉 java 我觉得未必。在析构函数中,不“释放”释放什么托管资源,指使用来释放非托管资源的。对于托管资源也根本不存在什么“释放内存”这一说,释放内存是GC做的事情,你根本不知道怎么释放。你写的对托管资源的close跟dispose调用这两个东西也根本就跟“释放内存”毫无关系。在你的程序中,哪有什么非托管资源的影子?你写析构函数干什么?
在过你真的需要及时关闭数据库连接,那么你可以写public class AccessDb : IDisposable
{
string connstr = "Provider=Microsoft.Jet.OLEDB.4.0 ;Data Source=C:\\test.mdb";
System.Data.OleDb.OleDbConnection conn = null;
public AccessDb()
{
conn = new System.Data.OleDb.OleDbConnection(connstr);
conn.Open();
} public void Dispose()
{
conn.Dispose();
}
}public partial class Form1 : Form
{
AccessDb db = null;
public Form1()
{
InitializeComponent();
db = new AccessDb();
db.Dispose();
}
}// 托管系统里的析构函数跟你所知道的c++的析构函数根本就是两回事。托管里的析构函数,被GC调用,而不是你的代码调用!它被调用之前有一大堆非常复杂的清理工作,然后把所有析构函数放到Finally队列里边去随机无序地处理,因此如果你写public class TA
{
~TA()
{
int x = 0;
}
}public class AccessDb
{
TA a;
string connstr = "Provider=Microsoft.Jet.OLEDB.4.0 ;Data Source=C:\\tmp\\finance.mdb";
System.Data.OleDb.OleDbConnection conn = null;
public AccessDb()
{
conn = new System.Data.OleDb.OleDbConnection(connstr);
conn.Open();
a = new TA();
} ~AccessDb()
{
conn.Close();
conn.Dispose();
}
}
你就会看到首先调用的是 TA 的析构函数,然后才是 AccessDb 的!由于你不知道这个,因此我相信你也不了解托管系统的析构函数,包括说,你肯定也不了解 java 的析构函数。
之所以出了问题,是因为底层的拉圾回收有个执行的顺序,你在Form窗体里定义了这个AccessDb类,类里定义的析构,GC的做法:
在 System.WeakReference.set_Target(Object value) 这一句
在 System.Data.ProviderBase.DbConnectionInternal.CloseConnection(DbConnection owningObject, DbConnectionFactory connectionFactory)
在 System.Data.OleDb.OleDbConnection.Close()
在 System.Data.OleDb.OleDbConnection.Dispose(Boolean disposing)
在 System.ComponentModel.Component.Dispose()
在 WindowsFormsApplication1.AccessDb.Finalize()
在执行GC 时, WeakReference的实列 this._owningObject.这个对象,他是序列化对象,是托管资源,就是说在执行GC执行时先把_owningObject 这个内存里的对象先回收了,然后再去执行析构函数里面的close方法,造成了变成了空引用了。就出现了上面的错误了。
SqlConnection由于实现了IDisposable接口,所以在垃圾回收阶段,会通过Dispose模式调用Close方法,但是这个时间我们是无法确定的,这一切都是由GC来确定的,所以为了保证在其他时候连接没有被占用,用完就关闭是个好习惯。你得先理解什么是IDisposable接口,不能用C++的路子来思考托管代码。
{
connection.Open();
//一段代码
}
通常我们这么用。请参考下面的链接
http://www.cnblogs.com/liuhaorain/archive/2012/02/15/2349886.html
再看看这个
我做程序牢记一条圣经:数据库连接是宝贵的不可再生的不受托管的资源,必须手工显式释放!也就是你自己拉的屎,你自己必须擦干净,否则必死无疑!怎么着?到了C#,数据库连接就变成托管资源了?C#很智能?能把数据库连接作为托管资源智能地释放?不用程序员Care了?
实践是检验真理的唯一标准。马上编程测试,测试程序如下:using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;namespace TestDb4
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
//如果数据库不存在表cache,可以用如下语句生成一个表
//AccessDb db=new AccessDb();
//db.UpdateData("create table Cache(id int primary key,log varchar(50));");
} private void ButtonTest_Click(object sender, EventArgs e)
{
int i, testTimes = 60000;
for (i = 0; i < testTimes; i++)
{
try
{
AccessDb db = new AccessDb();
int flag=db.UpdateData("delete from Cache");
if(flag<0)
{
string sError = "Error in i=" + i + ",sql failed";
MessageBox.Show(sError);
Console.WriteLine(sError);
break;
}
}
catch (Exception ex)
{
string sError = "Error in i=" + i + ",msg=" + ex.Message;
MessageBox.Show(sError);
Console.WriteLine(sError);
break;
}
}
if (i >= testTimes)
MessageBox.Show("Succeed!");
}
} public class AccessDb
{
string connstr = "Provider=Microsoft.Jet.OLEDB.4.0 ;Data Source=C:\\tmp\\finance.mdb";
System.Data.OleDb.OleDbConnection conn = null;
public AccessDb()
{
conn = new System.Data.OleDb.OleDbConnection(connstr);
conn.Open();
} /*
~AccessDb()
{
//UpdateData("delete from Cache");
conn.Close();
conn.Dispose();
}
*/
public int UpdateData(string sSQL)
{
int iResult = 0;
try
{
System.Data.OleDb.OleDbCommand comm = new System.Data.OleDb.OleDbCommand(sSQL, conn);
iResult = comm.ExecuteNonQuery();
//comm.Dispose();
}
catch (Exception ex)
{
Console.WriteLine("sql=" + sSQL + ",error=" + ex.Message);
iResult = -1;
}
return (iResult);
}
}
}
启动测试程序,在程序中点击“Test”按钮后就开始测试,根本做不到60000次循环。做到第64次时数据库资源就被耗尽并且报错了(报错信息:Error in i=64,msg=未指定的错误)。这说明在C#里数据库连接同样也是宝贵的不可再生的不受托管的资源。
{
Bid.PoolerTrace("<prov.DbConnectionInternal.CloseConnection|RES|CPOOL> %d# Closing.\n", this.ObjectID);
if (connectionFactory.SetInnerConnectionFrom(owningObject, DbConnectionOpenBusy.SingletonInstance, this))
{
try
{
DbConnectionPool pool = this.Pool;
Transaction enlistedTransaction = this.EnlistedTransaction;
if ((null != enlistedTransaction) && (enlistedTransaction.TransactionInformation.Status != TransactionStatus.Active))
{
this.DetachTransaction(enlistedTransaction);
}
if (pool != null)
{
pool.PutObject(this, owningObject);
}
else
{
this.Deactivate();
this.PerformanceCounters.HardDisconnectsPerSecond.Increment();
this._owningObject.Target = null;
if (this.IsTransactionRoot)
{
this.SetInStasis();
}
else
{
this.PerformanceCounters.NumberOfNonPooledConnections.Decrement();
if (base.GetType() != typeof(SqlInternalConnectionSmi))
{
this.Dispose();
}
}
}
}
finally
{
connectionFactory.SetInnerConnectionEvent(owningObject, DbConnectionClosedPreviouslyOpened.SingletonInstance);
}
}
}
internal virtual void CloseConnection(DbConnection owningObject, DbConnectionFactory connectionFactory)
{
Bid.PoolerTrace("<prov.DbConnectionInternal.CloseConnection|RES|CPOOL> %d# Closing.\n", this.ObjectID);
if (connectionFactory.SetInnerConnectionFrom(owningObject, DbConnectionOpenBusy.SingletonInstance, this))
{
try
{
DbConnectionPool pool = this.Pool;
Transaction enlistedTransaction = this.EnlistedTransaction;
if ((null != enlistedTransaction) && (enlistedTransaction.TransactionInformation.Status != TransactionStatus.Active))
{
this.DetachTransaction(enlistedTransaction);
}
if (pool != null)
{
pool.PutObject(this, owningObject);
}
else
{
this.Deactivate();
this.PerformanceCounters.HardDisconnectsPerSecond.Increment();
this._owningObject.Target = null; <----这句出益常了, 因为_owningObject 这个已被拉圾回收给销毁了。
if (this.IsTransactionRoot)
{
this.SetInStasis();
}
else
{
this.PerformanceCounters.NumberOfNonPooledConnections.Decrement();
if (base.GetType() != typeof(SqlInternalConnectionSmi))
{
this.Dispose();
}
}
}
}
finally
{
connectionFactory.SetInnerConnectionEvent(owningObject, DbConnectionClosedPreviouslyOpened.SingletonInstance);
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;namespace TestDb4
{
public class AccessDb
{
string connstr = "Provider=Microsoft.Jet.OLEDB.4.0 ;Data Source=C:\\tmp\\finance.mdb";
//System.Data.OleDb.OleDbConnection conn = null;
public AccessDb()
{
//conn = new System.Data.OleDb.OleDbConnection(connstr);
//conn.Open();
} /*
~AccessDb()
{
//UpdateData("delete from Cache");
//conn.Close();
//conn.Dispose();
}
*/ private System.Data.OleDb.OleDbConnection GetConnection()
{
System.Data.OleDb.OleDbConnection conn = new System.Data.OleDb.OleDbConnection(connstr);
conn.Open();
return(conn);
} public int UpdateData(string sSQL)
{
int iResult = 0;
System.Data.OleDb.OleDbConnection conn = null;
System.Data.OleDb.OleDbCommand cmd = null;
try
{
conn = GetConnection();
cmd = new System.Data.OleDb.OleDbCommand(sSQL, conn);
iResult = cmd.ExecuteNonQuery();
}
catch (Exception ex)
{
Console.WriteLine("sql=" + sSQL + ",error=" + ex.Message);
iResult = -1;
}
finally
{
CloseConnection(conn,cmd);
}
return (iResult);
} public void CloseConnection(System.Data.OleDb.OleDbConnection conn, System.Data.OleDb.OleDbCommand cmd)
{
try
{
if (cmd != null)
{
cmd.Dispose();
}
}
catch (Exception) { }
try
{
if (conn != null)
{
conn.Dispose();
}
}
catch (Exception) { }
}
}
}没办法,C#的回收机制存在重大缺陷,只有绕过它才能产生没有Bug的代码。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;namespace TestDb4
{
public class AccessDb
{
string connstr = "Provider=Microsoft.Jet.OLEDB.4.0 ;Data Source=C:\\tmp\\finance.mdb";
//System.Data.OleDb.OleDbConnection conn = null;
public AccessDb()
{
//conn = new System.Data.OleDb.OleDbConnection(connstr);
//conn.Open();
} /*
~AccessDb()
{
//UpdateData("delete from Cache");
//conn.Close();
//conn.Dispose();
}
*/ private System.Data.OleDb.OleDbConnection GetConnection()
{
System.Data.OleDb.OleDbConnection conn = new System.Data.OleDb.OleDbConnection(connstr);
conn.Open();
return(conn);
} public int UpdateData(string sSQL)
{
int iResult = 0;
System.Data.OleDb.OleDbConnection conn = null;
System.Data.OleDb.OleDbCommand cmd = null;
try
{
conn = GetConnection();
cmd = new System.Data.OleDb.OleDbCommand(sSQL, conn);
iResult = cmd.ExecuteNonQuery();
}
catch (Exception ex)
{
Console.WriteLine("sql=" + sSQL + ",error=" + ex.Message);
iResult = -1;
}
finally
{
CloseConnection(conn,cmd);
}
return (iResult);
} public void CloseConnection(System.Data.OleDb.OleDbConnection conn, System.Data.OleDb.OleDbCommand cmd)
{
try
{
if (cmd != null)
{
cmd.Dispose();
}
}
catch (Exception) { }
try
{
if (conn != null)
{
conn.Dispose();
}
}
catch (Exception) { }
}
}
}没办法,C#的回收机制存在重大缺陷,只有绕过它才能产生没有Bug的代码。不是C#回收存在重大缺陷,他的回收几制是没问题的,是刚好你所用的那个OledbConnecion类里面判断出了问题,
“因为C#的垃圾回收机制有点懒,不会马上回收无效的资源”这个不是C#的回收懒,回收会影响到性能,所以GC只有在必须要回收时才去回收,这是GC的回收策略,这样在影响系统资源最小的情况下回收。算你上面用了显式的Dispose,GC也不会马上自动回收内存的,也是要经过一段的条件后才处理的。除非你Dispose后自己马上手动执行了 GC.Collect();
if(conn!=null)
至于这个问题的发生应该是在你的析构函数之前C#已经将conn回收了。
因为.NET环境并不能保证析构操作的确切时间,因此不能将C++中的做法在C#中实现。在C#中,析构迟早都会执行,不过其执行时机却无法预料;
以上摘自C#高效编程
~AccessDb()
{
conn.Close();
conn.Dispose();
}这里可能是.net Bug 你确用这个BUG造就了一个更大的BUG.
When you are not sure if an object is existed, null check should be the first thing to do either with Java or C#. In fact, many bugs made by junior developer just like what lucky5168 did, invoking a property or a method on an no longer existed object which should be definitely avoided.
When you are not sure if an object is existed, null check should be the first thing to do either with Java or C#. In fact, many bugs made by junior developer just like what lucky5168 did, invoking a property or a method on an no longer existed object which should be definitely avoided.
Lao da please say ren hua....
用翻译机很累的。
SqlConnection由于实现了IDisposable接口,所以在垃圾回收阶段,会通过Dispose模式调用Close方法,但是这个时间我们是无法确定的,这一切都是由GC来确定的,所以为了保证在其他时候连接没有被占用,用完就关闭是个好习惯。你得先理解什么是IDisposable接口,不能用C++的路子来思考托管代码。我对这个习惯有看法,我认为这个要看软件的架构和应用什么数据库。上文中用Access数据库没有连接池在使用ODBCConnection.Close时就关闭了。但是SQL Server即使调用了SQLConnection.Close也不是真正的Logout了,还是要等到Dispose后GC回收了才断开,而且,下一个SQLConnection.Open的时候,如果前一个已经Close了就会接续使用而不是频繁的打开关闭数据库连接。这个从数据库后台抓一下Profiler就看得到了,它会自动发“exec sp_reset_connection”这个语句的来重置连接的。
所以,调用SQLconnection.close是需要的,但是手工调用Dispose就画蛇添足了。交给GC来处理就好了。而且,调用了Close只是表示把连接归还给了连接池,并没有真正的断开。
你的测试代码显示,可能是.net的bug;谁知道呢,根据msdn的解释,可能微软就是这么设计的
https://msdn.microsoft.com/zh-cn/library/66x5fx1b.aspx
GC机制虽然有不及时的情况。但是对一般程序而言,自动释放资源可以满足。
如果要自己释放,可以继承IDispose接口 去实现。
回收器不支持这个接口, 不必担心为空 并且可以访问其他对象操作释放前所有方法
试试 IDisposable与GC.SuppressFinalize();并用
释放资源可以用using 配合 Dispose 楼主对c#的理解很一般嘛.
语气还这么冲...
C#编写的程序,我认为很吃内存,尤其是读写数据库,所以常用GC.collect()来强制回收。
至于你说的conn.close(),做这个之前都要做判断的,至少我在C#上基本不用析构方法。