浏览代码

enable nullable on BaseClient and SftpClient (#1339)

* enable nullable on BaseClient and SftpClient

* SftpClient: use nameof for ArgumentException
mus65 1 年之前
父节点
当前提交
4cab7e5cf6
共有 4 个文件被更改,包括 67 次插入59 次删除
  1. 12 13
      src/Renci.SshNet/BaseClient.cs
  2. 2 1
      src/Renci.SshNet/IBaseClient.cs
  3. 13 12
      src/Renci.SshNet/ISftpClient.cs
  4. 40 33
      src/Renci.SshNet/SftpClient.cs

+ 12 - 13
src/Renci.SshNet/BaseClient.cs

@@ -1,4 +1,5 @@
-using System;
+#nullable enable
+using System;
 using System.Net.Sockets;
 using System.Threading;
 using System.Threading.Tasks;
@@ -22,7 +23,7 @@ namespace Renci.SshNet
         private readonly IServiceFactory _serviceFactory;
         private readonly object _keepAliveLock = new object();
         private TimeSpan _keepAliveInterval;
-        private Timer _keepAliveTimer;
+        private Timer? _keepAliveTimer;
         private ConnectionInfo _connectionInfo;
         private bool _isDisposed;
 
@@ -32,7 +33,7 @@ namespace Renci.SshNet
         /// <value>
         /// The current session.
         /// </value>
-        internal ISession Session { get; private set; }
+        internal ISession? Session { get; private set; }
 
         /// <summary>
         /// Gets the factory for creating new services.
@@ -142,17 +143,17 @@ namespace Renci.SshNet
         /// <summary>
         /// Occurs when an error occurred.
         /// </summary>
-        public event EventHandler<ExceptionEventArgs> ErrorOccurred;
+        public event EventHandler<ExceptionEventArgs>? ErrorOccurred;
 
         /// <summary>
         /// Occurs when host key received.
         /// </summary>
-        public event EventHandler<HostKeyEventArgs> HostKeyReceived;
+        public event EventHandler<HostKeyEventArgs>? HostKeyReceived;
 
         /// <summary>
         /// Occurs when server identification received.
         /// </summary>
-        public event EventHandler<SshIdentificationEventArgs> ServerIdentificationReceived;
+        public event EventHandler<SshIdentificationEventArgs>? ServerIdentificationReceived;
 
         /// <summary>
         /// Initializes a new instance of the <see cref="BaseClient"/> class.
@@ -193,7 +194,7 @@ namespace Renci.SshNet
                 throw new ArgumentNullException(nameof(serviceFactory));
             }
 
-            ConnectionInfo = connectionInfo;
+            _connectionInfo = connectionInfo;
             _ownsConnectionInfo = ownsConnectionInfo;
             _serviceFactory = serviceFactory;
             _keepAliveInterval = Timeout.InfiniteTimeSpan;
@@ -391,17 +392,17 @@ namespace Renci.SshNet
         {
         }
 
-        private void Session_ErrorOccured(object sender, ExceptionEventArgs e)
+        private void Session_ErrorOccured(object? sender, ExceptionEventArgs e)
         {
             ErrorOccurred?.Invoke(this, e);
         }
 
-        private void Session_HostKeyReceived(object sender, HostKeyEventArgs e)
+        private void Session_HostKeyReceived(object? sender, HostKeyEventArgs e)
         {
             HostKeyReceived?.Invoke(this, e);
         }
 
-        private void Session_ServerIdentificationReceived(object sender, SshIdentificationEventArgs e)
+        private void Session_ServerIdentificationReceived(object? sender, SshIdentificationEventArgs e)
         {
             ServerIdentificationReceived?.Invoke(this, e);
         }
@@ -432,14 +433,12 @@ namespace Renci.SshNet
 
                 Disconnect();
 
-                if (_ownsConnectionInfo && _connectionInfo is not null)
+                if (_ownsConnectionInfo)
                 {
                     if (_connectionInfo is IDisposable connectionInfoDisposable)
                     {
                         connectionInfoDisposable.Dispose();
                     }
-
-                    _connectionInfo = null;
                 }
 
                 _isDisposed = true;

+ 2 - 1
src/Renci.SshNet/IBaseClient.cs

@@ -1,4 +1,5 @@
-using System;
+#nullable enable
+using System;
 using System.Net.Sockets;
 using System.Threading;
 using System.Threading.Tasks;

+ 13 - 12
src/Renci.SshNet/ISftpClient.cs

@@ -1,4 +1,5 @@
-using System;
+#nullable enable
+using System;
 using System.Collections.Generic;
 using System.IO;
 using System.Text;
@@ -192,7 +193,7 @@ namespace Renci.SshNet
         /// <remarks>
         /// Method calls made by this method to <paramref name="output" />, may under certain conditions result in exceptions thrown by the stream.
         /// </remarks>
-        IAsyncResult BeginDownloadFile(string path, Stream output, AsyncCallback asyncCallback);
+        IAsyncResult BeginDownloadFile(string path, Stream output, AsyncCallback? asyncCallback);
 
         /// <summary>
         /// Begins an asynchronous file downloading into the stream.
@@ -211,7 +212,7 @@ namespace Renci.SshNet
         /// <remarks>
         /// Method calls made by this method to <paramref name="output" />, may under certain conditions result in exceptions thrown by the stream.
         /// </remarks>
-        IAsyncResult BeginDownloadFile(string path, Stream output, AsyncCallback asyncCallback, object state, Action<ulong> downloadCallback = null);
+        IAsyncResult BeginDownloadFile(string path, Stream output, AsyncCallback? asyncCallback, object? state, Action<ulong>? downloadCallback = null);
 
         /// <summary>
         /// Begins an asynchronous operation of retrieving list of files in remote directory.
@@ -224,7 +225,7 @@ namespace Renci.SshNet
         /// An <see cref="IAsyncResult" /> that references the asynchronous operation.
         /// </returns>
         /// <exception cref="ObjectDisposedException">The method was called after the client was disposed.</exception>
-        IAsyncResult BeginListDirectory(string path, AsyncCallback asyncCallback, object state, Action<int> listCallback = null);
+        IAsyncResult BeginListDirectory(string path, AsyncCallback? asyncCallback, object? state, Action<int>? listCallback = null);
 
         /// <summary>
         /// Begins the synchronize directories.
@@ -240,7 +241,7 @@ namespace Renci.SshNet
         /// <exception cref="ArgumentNullException"><paramref name="sourcePath"/> is <see langword="null"/>.</exception>
         /// <exception cref="ArgumentException"><paramref name="destinationPath"/> is <see langword="null"/> or contains only whitespace.</exception>
         /// <exception cref="SshException">If a problem occurs while copying the file.</exception>
-        IAsyncResult BeginSynchronizeDirectories(string sourcePath, string destinationPath, string searchPattern, AsyncCallback asyncCallback, object state);
+        IAsyncResult BeginSynchronizeDirectories(string sourcePath, string destinationPath, string searchPattern, AsyncCallback? asyncCallback, object? state);
 
         /// <summary>
         /// Begins an asynchronous uploading the stream into remote file.
@@ -289,7 +290,7 @@ namespace Renci.SshNet
         /// If the remote file already exists, it is overwritten and truncated.
         /// </para>
         /// </remarks>
-        IAsyncResult BeginUploadFile(Stream input, string path, AsyncCallback asyncCallback);
+        IAsyncResult BeginUploadFile(Stream input, string path, AsyncCallback? asyncCallback);
 
         /// <summary>
         /// Begins an asynchronous uploading the stream into remote file.
@@ -316,7 +317,7 @@ namespace Renci.SshNet
         /// If the remote file already exists, it is overwritten and truncated.
         /// </para>
         /// </remarks>
-        IAsyncResult BeginUploadFile(Stream input, string path, AsyncCallback asyncCallback, object state, Action<ulong> uploadCallback = null);
+        IAsyncResult BeginUploadFile(Stream input, string path, AsyncCallback? asyncCallback, object? state, Action<ulong>? uploadCallback = null);
 
         /// <summary>
         /// Begins an asynchronous uploading the stream into remote file.
@@ -343,7 +344,7 @@ namespace Renci.SshNet
         /// <see cref="SshException"/>.
         /// </para>
         /// </remarks>
-        IAsyncResult BeginUploadFile(Stream input, string path, bool canOverride, AsyncCallback asyncCallback, object state, Action<ulong> uploadCallback = null);
+        IAsyncResult BeginUploadFile(Stream input, string path, bool canOverride, AsyncCallback? asyncCallback, object? state, Action<ulong>? uploadCallback = null);
 
         /// <summary>
         /// Changes remote directory to path.
@@ -522,7 +523,7 @@ namespace Renci.SshNet
         /// <remarks>
         /// Method calls made by this method to <paramref name="output" />, may under certain conditions result in exceptions thrown by the stream.
         /// </remarks>
-        void DownloadFile(string path, Stream output, Action<ulong> downloadCallback = null);
+        void DownloadFile(string path, Stream output, Action<ulong>? downloadCallback = null);
 
         /// <summary>
         /// Ends an asynchronous file downloading into the stream.
@@ -698,7 +699,7 @@ namespace Renci.SshNet
         /// <exception cref="SftpPermissionDeniedException">Permission to list the contents of the directory was denied by the remote host. <para>-or-</para> A SSH command was denied by the server.</exception>
         /// <exception cref="SshException">A SSH error where <see cref="Exception.Message" /> is the message from the remote host.</exception>
         /// <exception cref="ObjectDisposedException">The method was called after the client was disposed.</exception>
-        IEnumerable<ISftpFile> ListDirectory(string path, Action<int> listCallback = null);
+        IEnumerable<ISftpFile> ListDirectory(string path, Action<int>? listCallback = null);
 
         /// <summary>
         /// Asynchronously enumerates the files in remote directory.
@@ -1004,7 +1005,7 @@ namespace Renci.SshNet
         /// <remarks>
         /// Method calls made by this method to <paramref name="input" />, may under certain conditions result in exceptions thrown by the stream.
         /// </remarks>
-        void UploadFile(Stream input, string path, Action<ulong> uploadCallback = null);
+        void UploadFile(Stream input, string path, Action<ulong>? uploadCallback = null);
 
         /// <summary>
         /// Uploads stream into remote file.
@@ -1022,7 +1023,7 @@ namespace Renci.SshNet
         /// <remarks>
         /// Method calls made by this method to <paramref name="input" />, may under certain conditions result in exceptions thrown by the stream.
         /// </remarks>
-        void UploadFile(Stream input, string path, bool canOverride, Action<ulong> uploadCallback = null);
+        void UploadFile(Stream input, string path, bool canOverride, Action<ulong>? uploadCallback = null);
 
         /// <summary>
         /// Writes the specified byte array to the specified file, and closes the file.

+ 40 - 33
src/Renci.SshNet/SftpClient.cs

@@ -1,4 +1,5 @@
-using System;
+#nullable enable
+using System;
 using System.Collections.Generic;
 using System.Diagnostics.CodeAnalysis;
 using System.Globalization;
@@ -26,7 +27,7 @@ namespace Renci.SshNet
         /// Holds the <see cref="ISftpSession"/> instance that is used to communicate to the
         /// SFTP server.
         /// </summary>
-        private ISftpSession _sftpSession;
+        private ISftpSession? _sftpSession;
 
         /// <summary>
         /// Holds the operation timeout.
@@ -170,7 +171,7 @@ namespace Renci.SshNet
         /// <value>
         /// The current SFTP session.
         /// </value>
-        internal ISftpSession SftpSession
+        internal ISftpSession? SftpSession
         {
             get { return _sftpSession; }
         }
@@ -369,7 +370,7 @@ namespace Renci.SshNet
 
             if (string.IsNullOrWhiteSpace(path))
             {
-                throw new ArgumentException("path");
+                throw new ArgumentException(nameof(path));
             }
 
             if (_sftpSession is null)
@@ -398,7 +399,7 @@ namespace Renci.SshNet
 
             if (string.IsNullOrWhiteSpace(path))
             {
-                throw new ArgumentException("path");
+                throw new ArgumentException(nameof(path));
             }
 
             if (_sftpSession is null)
@@ -429,7 +430,7 @@ namespace Renci.SshNet
 
             if (string.IsNullOrWhiteSpace(path))
             {
-                throw new ArgumentException("path");
+                throw new ArgumentException(nameof(path));
             }
 
             if (_sftpSession is null)
@@ -556,12 +557,12 @@ namespace Renci.SshNet
 
             if (string.IsNullOrWhiteSpace(path))
             {
-                throw new ArgumentException("path");
+                throw new ArgumentException(nameof(path));
             }
 
             if (string.IsNullOrWhiteSpace(linkPath))
             {
-                throw new ArgumentException("linkPath");
+                throw new ArgumentException(nameof(linkPath));
             }
 
             if (_sftpSession is null)
@@ -589,7 +590,7 @@ namespace Renci.SshNet
         /// <exception cref="SftpPermissionDeniedException">Permission to list the contents of the directory was denied by the remote host. <para>-or-</para> A SSH command was denied by the server.</exception>
         /// <exception cref="SshException">A SSH error where <see cref="Exception.Message" /> is the message from the remote host.</exception>
         /// <exception cref="ObjectDisposedException">The method was called after the client was disposed.</exception>
-        public IEnumerable<ISftpFile> ListDirectory(string path, Action<int> listCallback = null)
+        public IEnumerable<ISftpFile> ListDirectory(string path, Action<int>? listCallback = null)
         {
             CheckDisposed();
 
@@ -666,7 +667,7 @@ namespace Renci.SshNet
         /// An <see cref="IAsyncResult" /> that references the asynchronous operation.
         /// </returns>
         /// <exception cref="ObjectDisposedException">The method was called after the client was disposed.</exception>
-        public IAsyncResult BeginListDirectory(string path, AsyncCallback asyncCallback, object state, Action<int> listCallback = null)
+        public IAsyncResult BeginListDirectory(string path, AsyncCallback? asyncCallback, object? state, Action<int>? listCallback = null)
         {
             CheckDisposed();
 
@@ -758,7 +759,7 @@ namespace Renci.SshNet
 
             if (string.IsNullOrWhiteSpace(path))
             {
-                throw new ArgumentException("path");
+                throw new ArgumentException(nameof(path));
             }
 
             if (_sftpSession is null)
@@ -814,7 +815,7 @@ namespace Renci.SshNet
         /// <remarks>
         /// Method calls made by this method to <paramref name="output" />, may under certain conditions result in exceptions thrown by the stream.
         /// </remarks>
-        public void DownloadFile(string path, Stream output, Action<ulong> downloadCallback = null)
+        public void DownloadFile(string path, Stream output, Action<ulong>? downloadCallback = null)
         {
             CheckDisposed();
 
@@ -861,7 +862,7 @@ namespace Renci.SshNet
         /// <remarks>
         /// Method calls made by this method to <paramref name="output" />, may under certain conditions result in exceptions thrown by the stream.
         /// </remarks>
-        public IAsyncResult BeginDownloadFile(string path, Stream output, AsyncCallback asyncCallback)
+        public IAsyncResult BeginDownloadFile(string path, Stream output, AsyncCallback? asyncCallback)
         {
             return BeginDownloadFile(path, output, asyncCallback, state: null);
         }
@@ -883,13 +884,13 @@ namespace Renci.SshNet
         /// <remarks>
         /// Method calls made by this method to <paramref name="output" />, may under certain conditions result in exceptions thrown by the stream.
         /// </remarks>
-        public IAsyncResult BeginDownloadFile(string path, Stream output, AsyncCallback asyncCallback, object state, Action<ulong> downloadCallback = null)
+        public IAsyncResult BeginDownloadFile(string path, Stream output, AsyncCallback? asyncCallback, object? state, Action<ulong>? downloadCallback = null)
         {
             CheckDisposed();
 
             if (string.IsNullOrWhiteSpace(path))
             {
-                throw new ArgumentException("path");
+                throw new ArgumentException(nameof(path));
             }
 
             if (output is null)
@@ -951,7 +952,7 @@ namespace Renci.SshNet
         /// <remarks>
         /// Method calls made by this method to <paramref name="input" />, may under certain conditions result in exceptions thrown by the stream.
         /// </remarks>
-        public void UploadFile(Stream input, string path, Action<ulong> uploadCallback = null)
+        public void UploadFile(Stream input, string path, Action<ulong>? uploadCallback = null)
         {
             UploadFile(input, path, canOverride: true, uploadCallback);
         }
@@ -972,7 +973,7 @@ namespace Renci.SshNet
         /// <remarks>
         /// Method calls made by this method to <paramref name="input" />, may under certain conditions result in exceptions thrown by the stream.
         /// </remarks>
-        public void UploadFile(Stream input, string path, bool canOverride, Action<ulong> uploadCallback = null)
+        public void UploadFile(Stream input, string path, bool canOverride, Action<ulong>? uploadCallback = null)
         {
             CheckDisposed();
 
@@ -1040,7 +1041,7 @@ namespace Renci.SshNet
         /// If the remote file already exists, it is overwritten and truncated.
         /// </para>
         /// </remarks>
-        public IAsyncResult BeginUploadFile(Stream input, string path, AsyncCallback asyncCallback)
+        public IAsyncResult BeginUploadFile(Stream input, string path, AsyncCallback? asyncCallback)
         {
             return BeginUploadFile(input, path, canOverride: true, asyncCallback, state: null);
         }
@@ -1070,7 +1071,7 @@ namespace Renci.SshNet
         /// If the remote file already exists, it is overwritten and truncated.
         /// </para>
         /// </remarks>
-        public IAsyncResult BeginUploadFile(Stream input, string path, AsyncCallback asyncCallback, object state, Action<ulong> uploadCallback = null)
+        public IAsyncResult BeginUploadFile(Stream input, string path, AsyncCallback? asyncCallback, object? state, Action<ulong>? uploadCallback = null)
         {
             return BeginUploadFile(input, path, canOverride: true, asyncCallback, state, uploadCallback);
         }
@@ -1100,7 +1101,7 @@ namespace Renci.SshNet
         /// <see cref="SshException"/>.
         /// </para>
         /// </remarks>
-        public IAsyncResult BeginUploadFile(Stream input, string path, bool canOverride, AsyncCallback asyncCallback, object state, Action<ulong> uploadCallback = null)
+        public IAsyncResult BeginUploadFile(Stream input, string path, bool canOverride, AsyncCallback? asyncCallback, object? state, Action<ulong>? uploadCallback = null)
         {
             CheckDisposed();
 
@@ -1111,7 +1112,7 @@ namespace Renci.SshNet
 
             if (string.IsNullOrWhiteSpace(path))
             {
-                throw new ArgumentException("path");
+                throw new ArgumentException(nameof(path));
             }
 
             var flags = Flags.Write | Flags.Truncate;
@@ -1718,9 +1719,10 @@ namespace Renci.SshNet
 
             using (var stream = new StreamReader(OpenRead(path), encoding))
             {
-                while (!stream.EndOfStream)
+                string? line;
+                while ((line = stream.ReadLine()) != null)
                 {
-                    lines.Add(stream.ReadLine());
+                    lines.Add(line);
                 }
             }
 
@@ -2105,7 +2107,7 @@ namespace Renci.SshNet
 
             if (string.IsNullOrWhiteSpace(destinationPath))
             {
-                throw new ArgumentException("destinationPath");
+                throw new ArgumentException(nameof(destinationPath));
             }
 
             return InternalSynchronizeDirectories(sourcePath, destinationPath, searchPattern, asynchResult: null);
@@ -2122,10 +2124,10 @@ namespace Renci.SshNet
         /// <returns>
         /// An <see cref="IAsyncResult" /> that represents the asynchronous directory synchronization.
         /// </returns>
-        /// <exception cref="ArgumentNullException"><paramref name="sourcePath"/> is <see langword="null"/>.</exception>
+        /// <exception cref="ArgumentNullException"><paramref name="sourcePath"/> or <paramref name="searchPattern"/> is <see langword="null"/>.</exception>
         /// <exception cref="ArgumentException"><paramref name="destinationPath"/> is <see langword="null"/> or contains only whitespace.</exception>
         /// <exception cref="SshException">If a problem occurs while copying the file.</exception>
-        public IAsyncResult BeginSynchronizeDirectories(string sourcePath, string destinationPath, string searchPattern, AsyncCallback asyncCallback, object state)
+        public IAsyncResult BeginSynchronizeDirectories(string sourcePath, string destinationPath, string searchPattern, AsyncCallback? asyncCallback, object? state)
         {
             if (sourcePath is null)
             {
@@ -2134,7 +2136,12 @@ namespace Renci.SshNet
 
             if (string.IsNullOrWhiteSpace(destinationPath))
             {
-                throw new ArgumentException("destDir");
+                throw new ArgumentException(nameof(destinationPath));
+            }
+
+            if (searchPattern is null)
+            {
+                throw new ArgumentNullException(nameof(searchPattern));
             }
 
             var asyncResult = new SftpSynchronizeDirectoriesAsyncResult(asyncCallback, state);
@@ -2176,7 +2183,7 @@ namespace Renci.SshNet
             return ar.EndInvoke();
         }
 
-        private List<FileInfo> InternalSynchronizeDirectories(string sourcePath, string destinationPath, string searchPattern, SftpSynchronizeDirectoriesAsyncResult asynchResult)
+        private List<FileInfo> InternalSynchronizeDirectories(string sourcePath, string destinationPath, string searchPattern, SftpSynchronizeDirectoriesAsyncResult? asynchResult)
         {
             if (!Directory.Exists(sourcePath))
             {
@@ -2271,7 +2278,7 @@ namespace Renci.SshNet
         /// </returns>
         /// <exception cref="ArgumentNullException"><paramref name="path" /> is <see langword="null"/>.</exception>
         /// <exception cref="SshConnectionException">Client not connected.</exception>
-        private List<ISftpFile> InternalListDirectory(string path, SftpListDirectoryAsyncResult asyncResult, Action<int> listCallback)
+        private List<ISftpFile> InternalListDirectory(string path, SftpListDirectoryAsyncResult? asyncResult, Action<int>? listCallback)
         {
             if (path is null)
             {
@@ -2338,7 +2345,7 @@ namespace Renci.SshNet
         /// <exception cref="ArgumentNullException"><paramref name="output" /> is <see langword="null"/>.</exception>
         /// <exception cref="ArgumentException"><paramref name="path" /> is <see langword="null"/> or contains whitespace.</exception>
         /// <exception cref="SshConnectionException">Client not connected.</exception>
-        private void InternalDownloadFile(string path, Stream output, SftpDownloadAsyncResult asyncResult, Action<ulong> downloadCallback)
+        private void InternalDownloadFile(string path, Stream output, SftpDownloadAsyncResult? asyncResult, Action<ulong>? downloadCallback)
         {
             if (output is null)
             {
@@ -2347,7 +2354,7 @@ namespace Renci.SshNet
 
             if (string.IsNullOrWhiteSpace(path))
             {
-                throw new ArgumentException("path");
+                throw new ArgumentException(nameof(path));
             }
 
             if (_sftpSession is null)
@@ -2404,7 +2411,7 @@ namespace Renci.SshNet
         /// <exception cref="ArgumentNullException"><paramref name="input" /> is <see langword="null"/>.</exception>
         /// <exception cref="ArgumentException"><paramref name="path" /> is <see langword="null"/> or contains whitespace.</exception>
         /// <exception cref="SshConnectionException">Client not connected.</exception>
-        private void InternalUploadFile(Stream input, string path, Flags flags, SftpUploadAsyncResult asyncResult, Action<ulong> uploadCallback)
+        private void InternalUploadFile(Stream input, string path, Flags flags, SftpUploadAsyncResult? asyncResult, Action<ulong>? uploadCallback)
         {
             if (input is null)
             {
@@ -2413,7 +2420,7 @@ namespace Renci.SshNet
 
             if (string.IsNullOrWhiteSpace(path))
             {
-                throw new ArgumentException("path");
+                throw new ArgumentException(nameof(path));
             }
 
             if (_sftpSession is null)