Dispose、Close、=null三者之间的区别

单例模式与Dispose方法
 
上午想做个简单的单件设计模式(Singleton)的小例子,结果出现了一些意想不到的问题。我的本意是建立两个窗体类Form1和Form2,在Form2上放置一个名为Button1的按钮,单击Button1会调出Form1的窗口事例,并且保证再单击Button1的时候,不会再弹出第二个Form1实例,除非关掉Form1实例并重新单击Button1,总之,保证只能弹出一个Form1实例(当然不是用模态方式啦)。代码如下所示:
 
Form1类
       public class Form1
       {    
              private static Form1 frm;
              private Form1()//这个构造函数只允许类内调用,所以是私有的
              {
                     InitializeComponent();
              }
 
              public static Form1 CreateForm1()//该函数可带参数,自己想想如何实现
              {
                     if(frm == null)
                     {
                            frm = new Form1();
                            return frm;
                     }
                     else
                     {
                            return frm;
                     }
              }
       }
 
Form2类
              public class Form2
       {    
              ..... 
              private void button1_Click(object sender, System.EventArgs e)
              {
                     Form1 frm1 = Form1.CreateForm1();
                     frm1.Show();
              }
              .....
       }
于是问题出现了:我单击Form2窗体上的一个名为button1按钮要弹出Form1的窗口(函数如上),第一次可以弹出来,并且可以保证再单击button1时,不再弹出Form1的窗口,但是关闭Form1窗口实例后,再次单击button1按钮则出错,解释为无法访问名为“Form1”的已处置对象。这是什么原因?
经过细致的观察和向高手询问,终于搞清楚了问题的真相。这就是Dispose和null之间的区别。Dispose和Close是相同的,据说是因为有些类有Open的方法,所以为了对应才搞出了一个Close方法。Dispose()方法实际上是销毁了对象的实例,但是该对象变量仍然指向这块被销毁的内存地址上!而只要有所指向,它就绝不等于null!而若原来夫frm = new Form1();然后又frm = null;这意味着frm不再指向new出来的对象,他们之间的关系被“切断”了,frm成了一个没有指向的变量,但同时new出来的那个对象也占据着一块内存,它并没有被干掉!后来我又发现,窗体在执行Close()方法后会激发Closing事件,而使用dispose()方法则不会激发该事件。在singleton模式中,单例窗体的Closing事件里面,加入了“frm=null;”这么一句话,所以不要用dispose()关闭单例窗体,而要用Close()方法关闭。引申出来,所有拥有Close()方法的类,最好都用Close()方法关闭,而不要用Dispose()方法。
所以,上面的例子中,把frm1关掉后,它new出来的那个对象所占用的地址被dispose掉了,但是变量frm1仍然指向这块被dispose掉的地址!所以它也就不等于null!所以当再次单击Button1的时候,直接进入了else部分,导致了错误。那怎么解决呢?这么办,在Form1的Closed事件中将frm1设置成null,就可以了。因为关闭窗体,执行dispose的时候,窗体内的其他代码并不停止,直到执行完毕,所以,可以将frm1设置成null,完全没有问题。
这里还有另外一个问题。假如一个对象(自定义的类的实例)没有dispose掉,就把它设置成null,那这块内存怎么办呢?垃圾回收机制会自动收拾它的。但是垃圾回收机制,不能保证何时去回收它,所以你也不知道它到底啥时候能回收掉,这样可能影响系统的效率。解决方法是,使该类继承IDisposable接口,然后实现他的Dispose()方法。函数内写GC.SuppressFinalize(this);这样,如果手动调用dispose()方法,GC就不会通过析构函数再次销毁对象了。尤其当这个对象作为局部变量的时候,这样做是很有必要的。可以将dispose()写入finally{}语句中,更好的方法是使用using关键字把代码装入{}中。例如:
Using(Class1 cls1 = new Class())
{。。。。}
Using里的参数必须是实现了dispose方法的对象。当程序运行出{}时,自动销毁cls1(但是cls1!=null,这个已经反复说过多次了)。真正是实现的时候,可能还要考虑到多线程等诸多问题。
using Autodesk.Revit.DB; using Autodesk.Revit.UI; using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; using System.Windows.Forms.Integration; using Application = Autodesk.Revit.ApplicationServices.Application; namespace 水闸闸孔总净宽计算 { public partial class 计算原理与简图 : System.Windows.Forms.Form { ExternalCommandData dataform; string msgform; ElementSet eleform; UIApplication uiapp = null; UIDocument uidoc = null; Document doc = null; Application app = null; public 计算原理与简图(ExternalCommandData data, ref string msg, ElementSet ele) { InitializeComponent(); dataform = data; msgform = msg; eleform = ele; uiapp = data.Application; uidoc = uiapp.ActiveUIDocument; doc = uidoc.Document; } private 水闸计算 水闸计算; public 计算原理与简图(水闸计算 mainFormRef) { InitializeComponent(); this.水闸计算 = mainFormRef; // 保存引用 } PreviewControl previewControl = null; string pathname = typeof(定制按钮).Assembly.Location; string filename = ""; private void 计算原理与简图_Load(object sender, EventArgs e) { int wz = pathname.LastIndexOf(@"\"); string pathsjtxt = pathname.Substring(0, wz + 1);//pathsjtxt是数据表路径的意思 filename = pathsjtxt + "水闸计算样板.rvt"; Document newDoc = app.OpenDocumentFile(filename); View3D view3D = null; //找到当前文件的3D视图 FilteredElementCollector ViewCollector = new FilteredElementCollector(newDoc).OfClass(typeof(View3D)); foreach (View3D view in ViewCollector) { if ("{3D}".Equals(view.Name) || "三维".Equals(view.Name)) { view3D = view; break; } } previewControl = elementHost1.Child as PreviewControl; if (previewControl != null) { previewControl.Dispose(); } elementHost1.Child = new PreviewControl(newDoc, view3D.Id); previewControl = elementHost1.Child as PreviewControl; } } }运行时,Document newDoc = app.OpenDocumentFile(filename);这行代码显示为将对象设置引用到对象的实例
07-28
public void yz_Click(object sender, EventArgs e) { Session theSession = Session.GetSession(); Part displayPart = theSession.Parts.Display; if (displayPart != null && displayPart.ComponentAssembly != null) { TraverseAssemblyTopLevelOnly(displayPart.ComponentAssembly.RootComponent, theSession); } } private static void TraverseAssemblyTopLevelOnly(Component rootComponent, Session theSession) { foreach (Component child in rootComponent.GetChildren()) { if (child.IsSuppressed) { theSession.ListingWindow.WriteLine($"发现抑制组件: {child.DisplayName}"); // 获取抑制组件的原型 Part prototypePart = GetSuppressedComponentPrototypeAlternative(child, theSession); if (prototypePart != null) { theSession.ListingWindow.WriteLine($"成功获取原型: {prototypePart.FullPath}"); // 在这里对原型部件进行操作 // 操作完成后关闭部件,以便下次可以重新打开 ClosePartIfNotDisplay(prototypePart, theSession); } // 注意:这里不递归遍历抑制组件的子组件 } else { // 只有非抑制组件才递归遍历其子组件 if (child.GetChildren().Length > 0) { TraverseAssemblyTopLevelOnly(child, theSession); } } } } private static Part GetSuppressedComponentPrototypeAlternative(Component suppressedComp, Session theSession) { PartLoadStatus loadStatus = null; try { Part displayPart = theSession.Parts.Display; string displayPartPath = Path.GetDirectoryName(displayPart.FullPath); string suppressedCompName = suppressedComp.DisplayName; string presumedPartPath = Path.Combine(displayPartPath, suppressedCompName + ".prt"); // 检查部件是否已经打开 Part existingPart = FindPartByFullPath(presumedPartPath, theSession); if (existingPart != null) { theSession.ListingWindow.WriteLine($"部件已打开: {existingPart.FullPath}"); return existingPart; } // 使用OpenBaseDisplay作为替代方案,并传入loadStatus作为out参数 Part prototypePart = (Part)theSession.Parts.OpenBaseDisplay(presumedPartPath, out loadStatus); if (prototypePart != null) { theSession.ListingWindow.WriteLine($"使用OpenBaseDisplay成功打开部件: {prototypePart.FullPath}"); } return prototypePart; } catch (Exception ex) { theSession.ListingWindow.WriteLine($"替代方案也失败: {ex.Message}"); return null; } finally { // 清理PartLoadStatus资源 if (loadStatus != null) { loadStatus.Dispose(); } } } private static Part FindPartByFullPath(string fullPath, Session theSession) { try { // 遍历所有打开的部件,查找匹配的路径 PartCollection parts = theSession.Parts; foreach (Part part in parts) { if (part.FullPath.Equals(fullPath, StringComparison.OrdinalIgnoreCase)) { return part; } } return null; } catch (Exception ex) { theSession.ListingWindow.WriteLine($"查找部件时出错: {ex.Message}"); return null; } } private static void ClosePartIfNotDisplay(Part part, Session theSession) { try { // 不要关闭显示部件 if (part != null && part != theSession.Parts.Display && part != theSession.Parts.Work) { // 检查部件是否还有引用 if (CanClosePart(part)) { part.Close(BasePart.CloseWholeTree.False, BasePart.CloseModified.CloseModified, null); theSession.ListingWindow.WriteLine($"已关闭部件: {part.FullPath}"); } else { theSession.ListingWindow.WriteLine($"部件 {part.FullPath} 有其他引用,无法关闭"); } } } catch (Exception ex) { theSession.ListingWindow.WriteLine($"关闭部件时出错: {ex.Message}"); } } private static bool CanClosePart(Part part) { try { // 简单检查:如果部件不是显示部件或工作部件,并且没有未保存的修改,则可以关闭 // 您可以根据需要调整此逻辑 return !part.IsModified; } catch { return false; } } 以上代码yz_Click函数执行一次后,可以显示被抑制的文件,但是待抑制的文件关闭后,再次执行yz_Click函数没起作用,然后再次执行yz_Click就可以了。也就是说除了第一次执行yz_Click函数正常外,以后再用必须执行2次才能起作用,请改正
09-30
using System; using System.Collections.Generic; using System.Diagnostics; using System.IO.Ports; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; using MES.Device.Control; using MES.Manager; namespace TsHardWare { /// <summary> /// 条码枪 /// </summary> public class HardWare_ScanBar : HardWareBase { public delegate void DelegateReadBar(List<string> listBar); public event DelegateReadBar OnReadBar; public HardWare_ScanBar(string strIP, int port) : base(strIP, port) { } public HardWare_ScanBar(int _port, int _baudRate, Parity _parity, int _dataBits, StopBits _stopBits) : base(_port, _baudRate, _parity, _dataBits, _stopBits) { } /// <summary> /// 是否运行 /// </summary> public bool isRun = true; /// <summary> /// 一直读取条码串口或者tcp是否有数据回传 /// </summary> public void StartReading() { new Thread(() => { string strBarCode = ""; while (isRun) { Thread.Sleep(100); byte[] buff = null; if (HardWare.Read(ref buff) > 0) { strBarCode += Encoding.Default.GetString(buff); } if (strBarCode.Contains(EndLine)) //aaa\r\naaa\r\n { List<string> listBar = new List<string>(); string strLeft = ""; listBar = HardWareDataHelper.GetAllDataBySplit(strBarCode, EndLine, ref strLeft); if (listBar != null && listBar.Count != 0 && OnReadBar != null) { OnReadBar.BeginInvoke(listBar, null, null); strBarCode = strLeft; } } } }) { IsBackground = true }.Start(); } #region 字段 /// <summary> /// 每次读取的延时时间2s /// </summary> public int ReadBarCodeTimeOut = 5000; /// <summary> /// 每次读取的尝试次数 /// </summary> public int ReadTryTime = 3; /// <summary> /// 开命令 /// </summary> public string strOnCmd = ""; /// <summary> /// 关命令 /// </summary> public string strOffCmd; /// <summary> /// 发送是不是采用16进制协议 /// </summary> public bool OnHex;//发送是不是16进制的 public bool OffHex; /// <summary> /// 锁读取 /// </summary> private object lockRead = new object(); #endregion #region 读取设备条码 /// <summary> /// 读取设备条码 /// </summary> /// <param name="BarCode"></param> /// <returns></returns> public bool ReadEquipData(ref string BarCode, int Multiple = 1) { if (IsSimulater)//模拟数据 { Thread.Sleep(201); BarCode = "SimulaterCode" + DateTime.Now.ToString("yyyyMMddHHmmssfff"); return true; } if (HardWare == null) throw new Exception("[扫码]-协议基础未初始化!"); if (strOnCmd == null || strOnCmd == "") return false; try { if (HardWare.HareWareType == EnumHardWareTypes.RTU) { return ReadEquipDataRTU(Multiple, ref BarCode); } else if (HardWare.HareWareType == EnumHardWareTypes.TCP) { return ReadEquipDataTCP(Multiple, ref BarCode); } else { //WriteLog.LogError("ReadEquipData()->HardWare.HareWareType:" + HardWare.HareWareType.ToString()); } } catch (Exception ex) { //WriteLog.LogError("ReadEquipData()->EX:" + ex); } return false; } /// <summary> /// 读取设备条码 /// </summary> /// <param name="BarCode"></param> /// <returns></returns> public bool ReadEquipDataRTU(int Multiple, ref string BarCode) { if (IsSimulater)//模拟数据 { Thread.Sleep(201); BarCode = "ScanBar" + DateTime.Now.ToString("yyyyMMddHHmmssfff"); return true; } if (HardWare == null) throw new Exception("[扫码]-协议基础未初始化!"); if (strOnCmd == null || strOnCmd == "") return false; lock (lockRead) { try { if (!HardWare.IsConn) { HardWare.Connect(); } Stopwatch sw = new Stopwatch(); byte[] byteSend = null; if (OnHex) { byteSend = strOnCmd.String2HexByteArray(); } else { byteSend = Encoding.ASCII.GetBytes(strOnCmd); } HardWare.DiscardBuff(); HardWare.Write(byteSend);//开命令 //Thread.Sleep(20); //SendBarCodeOff(); //再发一个测量 sw.Reset(); sw.Start(); string strBarCode = ""; byte[] buff = null; while (sw.ElapsedMilliseconds < ReadBarCodeTimeOut) { Thread.Sleep(10); if (HardWare.Read(ref buff) > 0) { strBarCode += Encoding.Default.GetString(buff); // WriteLog.LogScan("扫描枪返回原始值:" + strBarCode); } if (strBarCode.EndsWith(EndLine)) //aaa\r\naaa\r\n { //WriteLog.LogScan("扫描枪返回原始值,带有结束符:" + strBarCode + EndLine); BarCode = HardWareDataHelper.GetMyDataByEndLine(strBarCode, EndLine); SendBarCodeOff(); return BarCode != "ERROR" && BarCode != (Multiple == 1 ? "ERROR" : "ERROR,ERROR"); ; } } SendBarCodeOff(); return false; } catch (Exception ex) { //WriteLog.LogError("ReadEquipData()->EX:" + ex); return false; } } } /// <summary> /// 读取设备条码 /// </summary> /// <param name="BarCode"></param> /// <returns></returns> public bool ReadEquipDataTCP(int Multiple, ref string BarCode) { if (IsSimulater)//模拟数据 { Thread.Sleep(201); BarCode = "ScanBar" + DateTime.Now.ToString("yyyyMMddHHmmssfff"); return true; } if (HardWare == null) throw new Exception("[扫码]-协议基础未初始化!"); if (strOnCmd == null || strOnCmd == "") return false; lock (lockRead) { try { HardWare.Close(); if (!HardWare.IsConn) { HardWare.Connect(); } Stopwatch sw = new Stopwatch(); byte[] byteSend = null; if (OnHex) { byteSend = strOnCmd.String2HexByteArray(); } else { byteSend = Encoding.ASCII.GetBytes(strOnCmd); } HardWare.DiscardBuff(); HardWare.Write(byteSend);//开命令 Thread.Sleep(20); //SendBarCodeOff(); //再发一个测量 sw.Reset(); sw.Start(); string strBarCode = ""; byte[] buff = null; while (sw.ElapsedMilliseconds < ReadBarCodeTimeOut) { Thread.Sleep(10); if (HardWare.Read(ref buff) > 0) { strBarCode += Encoding.Default.GetString(buff); } if (strBarCode.EndsWith(EndLine)) //aaa\r\naaa\r\n { BarCode = HardWareDataHelper.GetMyDataByEndLine(strBarCode, EndLine); //SendBarCodeOff(); //WriteLog.LogScan(Name + "读取扫码枪返回解析值:" + strBarCode); return BarCode != "ERROR" && BarCode != (Multiple == 1 ? "ERROR" : "ERROR,ERROR"); ; } } //SendBarCodeOff(); //rebot(); return false; } catch (Exception ex) { //WriteLog.LogError("ReadEquipData()->EX:" + ex); return false; } } } #endregion #region 扫码枪关闭命令-有的扫码枪需要读去完成后关闭条码枪 /// <summary> /// 扫码枪关闭命令 /// </summary> void SendBarCodeOff() { if (HardWare != null && strOffCmd != null && strOffCmd.Length != 0) { byte[] byteSend = null; if (OnHex) { byteSend = strOffCmd.String2HexByteArray(); } else { byteSend = Encoding.ASCII.GetBytes(strOffCmd); } //WriteLog.LogScan(OnHex + "关命令:" + strOffCmd); HardWare.Write(byteSend); } } #endregion } }帮我解释一下这个代码
10-06
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值