对比SerialCommunication和微软的SerialPort,向SerialPort看齐
2013-10-30 11:34
435 查看
SerialCommunication是我综合网上看到的代码稍作修改而成的串口通信类,而SerialPort则是C#的System类库的IO目录Ports子目录下的串口通信类。SerialCommunication只有区区的二百多行,而SerialPort则有几千行。下面我将介绍SerialPort相对于SerialCommunication好在哪里。
首先是一些基本字段的对比。
可以看到我有一个明显的不足,就是用了public的字段,这貌似是违反了面向对象的理念的,按照面向对象的理念,外部类应该是不能直接访问操作其他类的内部字段的,不过有些人就认为写private字段,public属性太麻烦,过度设计,能不用属性就不用属性。这里就不讨论用属性的优点了。除此之外还可以看到SerialPort有个明显的优点,使用了大量的重载方法,那些重载方法之间又用了继承,最终执行了参数最多的那个重载方法,该方法里面又将那些参数赋值给了对应的属性,各属性赋值时候又可以进行各种判断,这就是C#的经典套路,也是比较好的套路,只是这样写代码会长很多。
然后看串口打开方法
打开方法SerialPort比我的复杂很多,我那个只是打开并设置了一下串口,SerialPort除此之外还开启了ErrorReceived、PinChanged和DataReceived事件。两个接收事件和一个貌似是串口端口改变事件吧。前两个不写是因为我觉得有些人串口通信只需要写不需要接收,那么那两个事件也就不需要了吧,如果需要接收的话调用我那个Read方法就可以接收消息了,不过需要调用者自己开线程监听。
不说这个,就说代码方面的问题吧,我那里有个明显的不太好的地方就是,抛出的异常没有进行分类,异常是有格式的,也可以自定义异常,我那里没有对异常进行分类,全都是抛出Exception就完了,这样的话调用者就不能方便的根据异常的类型进行不同的处理了。除此之外抛出的异常也不够详细,SerialPort的Open方法一开始就有一个Port_already_open端口已打开异常,我那边就没有分的这么细了,只要端口打开失败就返回Port Open Failure,异常分的不够细也是不好的。
除此之外就是SerialPort用了大量的枚举,如StopBits、Handshake、Parity、SerialData、SerialError和SerialPinChange,用枚举有个很大的好处就是调用者可以清楚的根据枚举来判断每个枚举值的意义,这样子调用者可以在不查文档的情况下就顺利的写完了代码,这可能会节省编码人员很多的时间,与用枚举损失的些须效率,这是完全值得的,我没有用枚举只是贪图方便省事,这是不够好的,应该注意一下。
接着看串口关闭方法
关闭方法就完全不同了,我那关闭方法只是把串口关闭,SerialPort则貌似是把整个类Dispose了,这两种做法萝卜青菜各有所爱吧。不过我那个类貌似没有继承自IDisposable貌似不太好,比较大的类应该要提供Dispose才好的吧。
最后看看读写方法吧
对比可以得出我那个读写方法没有SerialPort那么多,我那里只提供了byte[]的读写,这是相对来说比较不够用的,不过这个不是问题。问题是我那里读写时的异常判断太少,只判断了串口关闭的情况,这样有些异常就不能在收发之前被判断出来了。
总结来说就是自己写的代码面向对象得还不够,代码不够规范,考虑的不够全面等,希望以后可以学着向“SerialPort”看齐!
首先是一些基本字段的对比。
/// <summary> /// 波特率 /// </summary> public int BaudRate; /// <summary> /// 数据位 /// </summary> public byte ByteSize = 8; /// <summary> /// 奇偶校验,0-4=no,odd,even,mark,space /// </summary> public byte Parity = 1; /// <summary> /// 串口号 /// </summary> public int PortNum; /// <summary> /// 读超时 /// </summary> public UInt32 ReadTimeout = 1000; /// <summary> /// 停止位,0,1,2 = 1, 1.5, 2 /// </summary> public byte StopBits;
public SerialPort() { this.baudRate = 0x2580; this.dataBits = 8; this.stopBits = System.IO.Ports.StopBits.One; this.portName = "COM1"; this.encoding = System.Text.Encoding.ASCII; this.decoder = System.Text.Encoding.ASCII.GetDecoder(); this.maxByteCountForSingleChar = System.Text.Encoding.ASCII.GetMaxByteCount(1); this.readTimeout = -1; this.writeTimeout = -1; this.receivedBytesThreshold = 1; this.parityReplace = 0x3f; this.newLine = "\n"; this.readBufferSize = 0x1000; this.writeBufferSize = 0x800; this.inBuffer = new byte[0x400]; this.oneChar = new char[1]; } public SerialPort(IContainer container) { this.baudRate = 0x2580; this.dataBits = 8; this.stopBits = System.IO.Ports.StopBits.One; this.portName = "COM1"; this.encoding = System.Text.Encoding.ASCII; this.decoder = System.Text.Encoding.ASCII.GetDecoder(); this.maxByteCountForSingleChar = System.Text.Encoding.ASCII.GetMaxByteCount(1); this.readTimeout = -1; this.writeTimeout = -1; this.receivedBytesThreshold = 1; this.parityReplace = 0x3f; this.newLine = "\n"; this.readBufferSize = 0x1000; this.writeBufferSize = 0x800; this.inBuffer = new byte[0x400]; this.oneChar = new char[1]; container.Add(this); } [TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")] public SerialPort(string portName) : this(portName, 0x2580, System.IO.Ports.Parity.None, 8, System.IO.Ports.StopBits.One) { } [TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")] public SerialPort(string portName, int baudRate) : this(portName, baudRate, System.IO.Ports.Parity.None, 8, System.IO.Ports.StopBits.One) { } [TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")] public SerialPort(string portName, int baudRate, System.IO.Ports.Parity parity) : this(portName, baudRate, parity, 8, System.IO.Ports.StopBits.One) { } [TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")] public SerialPort(string portName, int baudRate, System.IO.Ports.Parity parity, int dataBits) : this(portName, baudRate, parity, dataBits, System.IO.Ports.StopBits.One) { } public SerialPort(string portName, int baudRate, System.IO.Ports.Parity parity, int dataBits, System.IO.Ports.StopBits stopBits) { this.baudRate = 0x2580; this.dataBits = 8; this.stopBits = System.IO.Ports.StopBits.One; this.portName = "COM1"; this.encoding = System.Text.Encoding.ASCII; this.decoder = System.Text.Encoding.ASCII.GetDecoder(); this.maxByteCountForSingleChar = System.Text.Encoding.ASCII.GetMaxByteCount(1); this.readTimeout = -1; this.writeTimeout = -1; this.receivedBytesThreshold = 1; this.parityReplace = 0x3f; this.newLine = "\n"; this.readBufferSize = 0x1000; this.writeBufferSize = 0x800; this.inBuffer = new byte[0x400]; this.oneChar = new char[1]; this.PortName = portName; this.BaudRate = baudRate; this.Parity = parity; this.DataBits = dataBits; this.StopBits = stopBits; }
可以看到我有一个明显的不足,就是用了public的字段,这貌似是违反了面向对象的理念的,按照面向对象的理念,外部类应该是不能直接访问操作其他类的内部字段的,不过有些人就认为写private字段,public属性太麻烦,过度设计,能不用属性就不用属性。这里就不讨论用属性的优点了。除此之外还可以看到SerialPort有个明显的优点,使用了大量的重载方法,那些重载方法之间又用了继承,最终执行了参数最多的那个重载方法,该方法里面又将那些参数赋值给了对应的属性,各属性赋值时候又可以进行各种判断,这就是C#的经典套路,也是比较好的套路,只是这样写代码会长很多。
然后看串口打开方法
public void Open() { // OPEN THE COMM PORT. _hComm = CreateFile("COM" + PortNum, GenericRead | GenericWrite, 0, 0, OpenExisting, 0, 0); // IF THE PORT CANNOT BE OPENED, BAIL OUT. if (_hComm == InvalidHandleValue) { throw (new Exception("Port Open Failure")); } CommTimeouts ctoCommPort = new CommTimeouts { ReadTotalTimeoutConstant = ReadTimeout }; if (!SetCommTimeouts(_hComm, ref ctoCommPort)) { throw (new Exception("Bad Timeout Settings")); } Dcb dcb = new Dcb(); GetCommState(_hComm, ref dcb); dcb.BaudRate = BaudRate; dcb.Parity = Parity; dcb.ByteSize = ByteSize; dcb.StopBits = StopBits; if (!SetCommState(_hComm, ref dcb)) { throw (new Exception("Bad Com Settings")); } }
public void Open() { if (this.IsOpen) { throw new InvalidOperationException(SR.GetString("Port_already_open")); } new SecurityPermission(SecurityPermissionFlag.UnmanagedCode).Demand(); this.internalSerialStream = new SerialStream(this.portName, this.baudRate, this.parity, this.dataBits, this.stopBits, this.readTimeout, this.writeTimeout, this.handshake, this.dtrEnable, this.rtsEnable, this.discardNull, this.parityReplace); this.internalSerialStream.SetBufferSizes(this.readBufferSize, this.writeBufferSize); this.internalSerialStream.ErrorReceived += new SerialErrorReceivedEventHandler(this.CatchErrorEvents); this.internalSerialStream.PinChanged += new SerialPinChangedEventHandler(this.CatchPinChangedEvents); this.internalSerialStream.DataReceived += new SerialDataReceivedEventHandler(this.CatchReceivedEvents); } internal SerialStream(string portName, int baudRate, System.IO.Ports.Parity parity, int dataBits, System.IO.Ports.StopBits stopBits, int readTimeout, int writeTimeout, System.IO.Ports.Handshake handshake, bool dtrEnable, bool rtsEnable, bool discardNull, byte parityReplace) { int dwFlagsAndAttributes = 0x40000000; if (Environment.OSVersion.Platform == PlatformID.Win32Windows) { dwFlagsAndAttributes = 0x80; this.isAsync = false; } if ((portName == null) || !portName.StartsWith("COM", StringComparison.OrdinalIgnoreCase)) { throw new ArgumentException(SR.GetString("Arg_InvalidSerialPort"), "portName"); } SafeFileHandle hFile = Microsoft.Win32.UnsafeNativeMethods.CreateFile(@"\\.\" + portName, -1073741824, 0, IntPtr.Zero, 3, dwFlagsAndAttributes, IntPtr.Zero); if (hFile.IsInvalid) { InternalResources.WinIOError(portName); } try { int fileType = Microsoft.Win32.UnsafeNativeMethods.GetFileType(hFile); if ((fileType != 2) && (fileType != 0)) { throw new ArgumentException(SR.GetString("Arg_InvalidSerialPort"), "portName"); } this._handle = hFile; this.portName = portName; this.handshake = handshake; this.parityReplace = parityReplace; this.tempBuf = new byte[1]; this.commProp = new Microsoft.Win32.UnsafeNativeMethods.COMMPROP(); int lpModemStat = 0; if (!Microsoft.Win32.UnsafeNativeMethods.GetCommProperties(this._handle, ref this.commProp) || !Microsoft.Win32.UnsafeNativeMethods.GetCommModemStatus(this._handle, ref lpModemStat)) { int errorCode = Marshal.GetLastWin32Error(); switch (errorCode) { case 0x57: case 6: throw new ArgumentException(SR.GetString("Arg_InvalidSerialPortExtended"), "portName"); } InternalResources.WinIOError(errorCode, string.Empty); } if ((this.commProp.dwMaxBaud != 0) && (baudRate > this.commProp.dwMaxBaud)) { throw new ArgumentOutOfRangeException("baudRate", SR.GetString("Max_Baud", new object[] { this.commProp.dwMaxBaud })); } this.comStat = new Microsoft.Win32.UnsafeNativeMethods.COMSTAT(); this.dcb = new Microsoft.Win32.UnsafeNativeMethods.DCB(); this.InitializeDCB(baudRate, parity, dataBits, stopBits, discardNull); this.DtrEnable = dtrEnable; this.rtsEnable = this.GetDcbFlag(12) == 1; if ((handshake != System.IO.Ports.Handshake.RequestToSend) && (handshake != System.IO.Ports.Handshake.RequestToSendXOnXOff)) { this.RtsEnable = rtsEnable; } if (readTimeout == 0) { this.commTimeouts.ReadTotalTimeoutConstant = 0; this.commTimeouts.ReadTotalTimeoutMultiplier = 0; this.commTimeouts.ReadIntervalTimeout = -1; } else if (readTimeout == -1) { this.commTimeouts.ReadTotalTimeoutConstant = -2; this.commTimeouts.ReadTotalTimeoutMultiplier = -1; this.commTimeouts.ReadIntervalTimeout = -1; } else { this.commTimeouts.ReadTotalTimeoutConstant = readTimeout; this.commTimeouts.ReadTotalTimeoutMultiplier = -1; this.commTimeouts.ReadIntervalTimeout = -1; } this.commTimeouts.WriteTotalTimeoutMultiplier = 0; this.commTimeouts.WriteTotalTimeoutConstant = (writeTimeout == -1) ? 0 : writeTimeout; if (!Microsoft.Win32.UnsafeNativeMethods.SetCommTimeouts(this._handle, ref this.commTimeouts)) { InternalResources.WinIOError(); } if (this.isAsync && !ThreadPool.BindHandle(this._handle)) { throw new IOException(SR.GetString("IO_BindHandleFailed")); } Microsoft.Win32.UnsafeNativeMethods.SetCommMask(this._handle, 0x1fb); this.eventRunner = new EventLoopRunner(this); new Thread(new ThreadStart(this.eventRunner.WaitForCommEvent)) { IsBackground = true }.Start(); } catch { hFile.Close(); this._handle = null; throw; } }
打开方法SerialPort比我的复杂很多,我那个只是打开并设置了一下串口,SerialPort除此之外还开启了ErrorReceived、PinChanged和DataReceived事件。两个接收事件和一个貌似是串口端口改变事件吧。前两个不写是因为我觉得有些人串口通信只需要写不需要接收,那么那两个事件也就不需要了吧,如果需要接收的话调用我那个Read方法就可以接收消息了,不过需要调用者自己开线程监听。
不说这个,就说代码方面的问题吧,我那里有个明显的不太好的地方就是,抛出的异常没有进行分类,异常是有格式的,也可以自定义异常,我那里没有对异常进行分类,全都是抛出Exception就完了,这样的话调用者就不能方便的根据异常的类型进行不同的处理了。除此之外抛出的异常也不够详细,SerialPort的Open方法一开始就有一个Port_already_open端口已打开异常,我那边就没有分的这么细了,只要端口打开失败就返回Port Open Failure,异常分的不够细也是不好的。
除此之外就是SerialPort用了大量的枚举,如StopBits、Handshake、Parity、SerialData、SerialError和SerialPinChange,用枚举有个很大的好处就是调用者可以清楚的根据枚举来判断每个枚举值的意义,这样子调用者可以在不查文档的情况下就顺利的写完了代码,这可能会节省编码人员很多的时间,与用枚举损失的些须效率,这是完全值得的,我没有用枚举只是贪图方便省事,这是不够好的,应该注意一下。
接着看串口关闭方法
public void Close() { if (_hComm != InvalidHandleValue) { CloseHandle(_hComm); } }
public void Close() { base.Dispose(); } public void Dispose() { this.Dispose(true); GC.SuppressFinalize(this); } protected virtual void Dispose(bool disposing) { if (disposing) { lock (this) { if ((this.site != null) && (this.site.Container != null)) { this.site.Container.Remove(this); } if (this.events != null) { EventHandler handler = (EventHandler) this.events[EventDisposed]; if (handler != null) { handler(this, EventArgs.Empty); } } } } }
关闭方法就完全不同了,我那关闭方法只是把串口关闭,SerialPort则貌似是把整个类Dispose了,这两种做法萝卜青菜各有所爱吧。不过我那个类貌似没有继承自IDisposable貌似不太好,比较大的类应该要提供Dispose才好的吧。
最后看看读写方法吧
public byte[] Read(int numBytes) { if (_hComm == InvalidHandleValue) { throw (new Exception("Comm Port Not Open")); } int bytesRead = 0; byte[] bufBytes = new byte[numBytes]; ReadFile(_hComm, bufBytes, numBytes, ref bytesRead, ref _ovlCommPort); byte[] outBytes = new byte[bytesRead]; Array.Copy(bufBytes, outBytes, bytesRead); return outBytes; } public void Write(byte[] writeBytes) { if (_hComm == InvalidHandleValue) { throw (new Exception("Comm Port Not Open")); } WriteFile(_hComm, writeBytes, writeBytes.Length, ref _bytesCount, ref _ovlCommPort); }
public int Read(byte[] buffer, int offset, int count) { if (!this.IsOpen) { throw new InvalidOperationException(SR.GetString("Port_not_open")); } if (buffer == null) { throw new ArgumentNullException("buffer", SR.GetString("ArgumentNull_Buffer")); } if (offset < 0) { throw new ArgumentOutOfRangeException("offset", SR.GetString("ArgumentOutOfRange_NeedNonNegNumRequired")); } if (count < 0) { throw new ArgumentOutOfRangeException("count", SR.GetString("ArgumentOutOfRange_NeedNonNegNumRequired")); } if ((buffer.Length - offset) < count) { throw new ArgumentException(SR.GetString("Argument_InvalidOffLen")); } int num = 0; if (this.CachedBytesToRead >= 1) { num = Math.Min(this.CachedBytesToRead, count); Buffer.BlockCopy(this.inBuffer, this.readPos, buffer, offset, num); this.readPos += num; if (num == count) { if (this.readPos == this.readLen) { this.readPos = this.readLen = 0; } return count; } if (this.BytesToRead == 0) { return num; } } this.readLen = this.readPos = 0; int num2 = count - num; num += this.internalSerialStream.Read(buffer, offset + num, num2); this.decoder.Reset(); return num; } public int Read(char[] buffer, int offset, int count) { if (!this.IsOpen) { throw new InvalidOperationException(SR.GetString("Port_not_open")); } if (buffer == null) { throw new ArgumentNullException("buffer", SR.GetString("ArgumentNull_Buffer")); } if (offset < 0) { throw new ArgumentOutOfRangeException("offset", SR.GetString("ArgumentOutOfRange_NeedNonNegNumRequired")); } if (count < 0) { throw new ArgumentOutOfRangeException("count", SR.GetString("ArgumentOutOfRange_NeedNonNegNumRequired")); } if ((buffer.Length - offset) < count) { throw new ArgumentException(SR.GetString("Argument_InvalidOffLen")); } return this.InternalRead(buffer, offset, count, this.readTimeout, false); } public void Write(string text) { if (!this.IsOpen) { throw new InvalidOperationException(SR.GetString("Port_not_open")); } if (text == null) { throw new ArgumentNullException("text"); } if (text.Length != 0) { byte[] bytes = this.encoding.GetBytes(text); this.internalSerialStream.Write(bytes, 0, bytes.Length, this.writeTimeout); } } public void Write(byte[] buffer, int offset, int count) { if (!this.IsOpen) { throw new InvalidOperationException(SR.GetString("Port_not_open")); } if (buffer == null) { throw new ArgumentNullException("buffer", SR.GetString("ArgumentNull_Buffer")); } if (offset < 0) { throw new ArgumentOutOfRangeException("offset", SR.GetString("ArgumentOutOfRange_NeedNonNegNumRequired")); } if (count < 0) { throw new ArgumentOutOfRangeException("count", SR.GetString("ArgumentOutOfRange_NeedNonNegNumRequired")); } if ((buffer.Length - offset) < count) { throw new ArgumentException(SR.GetString("Argument_InvalidOffLen")); } if (buffer.Length != 0) { this.internalSerialStream.Write(buffer, offset, count, this.writeTimeout); } } public void Write(char[] buffer, int offset, int count) { if (!this.IsOpen) { throw new InvalidOperationException(SR.GetString("Port_not_open")); } if (buffer == null) { throw new ArgumentNullException("buffer"); } if (offset < 0) { throw new ArgumentOutOfRangeException("offset", SR.GetString("ArgumentOutOfRange_NeedNonNegNumRequired")); } if (count < 0) { throw new ArgumentOutOfRangeException("count", SR.GetString("ArgumentOutOfRange_NeedNonNegNumRequired")); } if ((buffer.Length - offset) < count) { throw new ArgumentException(SR.GetString("Argument_InvalidOffLen")); } if (buffer.Length != 0) { byte[] buffer2 = this.Encoding.GetBytes(buffer, offset, count); this.Write(buffer2, 0, buffer2.Length); } } public void WriteLine(string text) { this.Write(text + this.NewLine); }
对比可以得出我那个读写方法没有SerialPort那么多,我那里只提供了byte[]的读写,这是相对来说比较不够用的,不过这个不是问题。问题是我那里读写时的异常判断太少,只判断了串口关闭的情况,这样有些异常就不能在收发之前被判断出来了。
总结来说就是自己写的代码面向对象得还不够,代码不够规范,考虑的不够全面等,希望以后可以学着向“SerialPort”看齐!
相关文章推荐
- [转]微软SerialPort秘籍[SerialPort为什么死锁程序的分析]
- 微软SerialPort秘籍,SerialPort为什么死锁程序的分析
- 微软SerialPort秘籍[SerialPort为什么死锁程序的分析]
- SerialPort (RS-232 Serial COM Port) in C# .NET
- VC SerialPort串口调试,报错: SerialPort comstat is being used without defining
- S 串口编程 详解2 CSerialPort类 (SerialPort.h头文件和SerialPort.ccp实现文件)
- 《SerialPort (RS-232 Serial COM Port) in C# .NET》in Coad's Code
- SerialPort (RS-232 Serial COM Port) in C# .NET
- java.lang.ClassCastException: android.app.Application cannot be cast to serialport_api.sample.App问题
- WIZnet推出虚拟串口(Virtual Serial Port)
- Qt: 基于qextserialport实现的一个小串口工具
- 串口通讯demo-serial port communication 供参考
- 0804_serial port
- C# SerialPort 的“已关闭SafeHandle”
- [收藏]使用VMWare串口连接Windbg 官方文档说明 Examples: Debugging over a Virtual Serial Port
- VB.net学习笔记之串口通讯:System.IO.Ports.SerialPort
- SerialPort 同步和异步数据读取
- windows caffe(编译微软自己port的caffe)
- the setting of serial port in the SecureCRT
- C# SerialPort常用的属性和方法