55using System . Diagnostics . CodeAnalysis ;
66using System . Globalization ;
77using System . IO ;
8+ using System . Linq ;
89using System . Net ;
910using System . Runtime . CompilerServices ;
1011using System . Runtime . ExceptionServices ;
@@ -1727,7 +1728,7 @@ public byte[] ReadAllBytes(string path)
17271728 using ( var stream = OpenRead ( path ) )
17281729 {
17291730 var buffer = new byte [ stream . Length ] ;
1730- _ = stream . Read ( buffer , 0 , buffer . Length ) ;
1731+ stream . ReadExactly ( buffer , 0 , buffer . Length ) ;
17311732 return buffer ;
17321733 }
17331734 }
@@ -1760,23 +1761,7 @@ public string[] ReadAllLines(string path)
17601761 /// <exception cref="ObjectDisposedException">The method was called after the client was disposed.</exception>
17611762 public string [ ] ReadAllLines ( string path , Encoding encoding )
17621763 {
1763- /*
1764- * We use the default buffer size for StreamReader - which is 1024 bytes - and the configured buffer size
1765- * for the SftpFileStream. We may want to revisit this later.
1766- */
1767-
1768- var lines = new List < string > ( ) ;
1769-
1770- using ( var stream = new StreamReader ( OpenRead ( path ) , encoding ) )
1771- {
1772- string ? line ;
1773- while ( ( line = stream . ReadLine ( ) ) != null )
1774- {
1775- lines . Add ( line ) ;
1776- }
1777- }
1778-
1779- return lines . ToArray ( ) ;
1764+ return ReadLines ( path , encoding ) . ToArray ( ) ;
17801765 }
17811766
17821767 /// <summary>
@@ -1807,15 +1792,8 @@ public string ReadAllText(string path)
18071792 /// <exception cref="ObjectDisposedException">The method was called after the client was disposed.</exception>
18081793 public string ReadAllText ( string path , Encoding encoding )
18091794 {
1810- /*
1811- * We use the default buffer size for StreamReader - which is 1024 bytes - and the configured buffer size
1812- * for the SftpFileStream. We may want to revisit this later.
1813- */
1814-
1815- using ( var stream = new StreamReader ( OpenRead ( path ) , encoding ) )
1816- {
1817- return stream . ReadToEnd ( ) ;
1818- }
1795+ using var sr = new StreamReader ( OpenRead ( path ) , encoding ) ;
1796+ return sr . ReadToEnd ( ) ;
18191797 }
18201798
18211799 /// <summary>
@@ -1825,12 +1803,16 @@ public string ReadAllText(string path, Encoding encoding)
18251803 /// <returns>
18261804 /// The lines of the file.
18271805 /// </returns>
1828- /// <exception cref="ArgumentNullException"><paramref name="path"/> is <see langword="null"/>.</exception>
1829- /// <exception cref="SshConnectionException">Client is not connected.</exception>
1830- /// <exception cref="ObjectDisposedException">The method was called after the client was disposed.</exception>
1806+ /// <remarks>
1807+ /// The lines are enumerated lazily. The opening of the file and any resulting exceptions occur
1808+ /// upon enumeration.
1809+ /// </remarks>
1810+ /// <exception cref="ArgumentNullException"><paramref name="path"/> is <see langword="null"/>. Thrown eagerly.</exception>
1811+ /// <exception cref="SshConnectionException">Client is not connected upon enumeration.</exception>
1812+ /// <exception cref="ObjectDisposedException">The return value is enumerated after the client is disposed.</exception>
18311813 public IEnumerable < string > ReadLines ( string path )
18321814 {
1833- return ReadAllLines ( path ) ;
1815+ return ReadLines ( path , Encoding . UTF8 ) ;
18341816 }
18351817
18361818 /// <summary>
@@ -1841,12 +1823,36 @@ public IEnumerable<string> ReadLines(string path)
18411823 /// <returns>
18421824 /// The lines of the file.
18431825 /// </returns>
1844- /// <exception cref="ArgumentNullException"><paramref name="path"/> is <see langword="null"/>.</exception>
1845- /// <exception cref="SshConnectionException">Client is not connected.</exception>
1846- /// <exception cref="ObjectDisposedException">The method was called after the client was disposed.</exception>
1826+ /// <remarks>
1827+ /// The lines are enumerated lazily. The opening of the file and any resulting exceptions occur
1828+ /// upon enumeration.
1829+ /// </remarks>
1830+ /// <exception cref="ArgumentNullException"><paramref name="path"/> is <see langword="null"/>. Thrown eagerly.</exception>
1831+ /// <exception cref="SshConnectionException">Client is not connected upon enumeration.</exception>
1832+ /// <exception cref="ObjectDisposedException">The return value is enumerated after the client is disposed.</exception>
18471833 public IEnumerable < string > ReadLines ( string path , Encoding encoding )
18481834 {
1849- return ReadAllLines ( path , encoding ) ;
1835+ // We allow this usage exception to throw eagerly...
1836+ ThrowHelper . ThrowIfNull ( path ) ;
1837+
1838+ // ... but other exceptions will throw lazily i.e. inside the state machine created
1839+ // by yield. We could choose to open the file eagerly as well in order to throw
1840+ // file-related exceptions eagerly (matching what File.ReadLines does), but this
1841+ // complicates double enumeration, and introduces the problem that File.ReadLines
1842+ // has whereby the file is not closed if the return value is not enumerated.
1843+ return Enumerate ( ) ;
1844+
1845+ IEnumerable < string > Enumerate ( )
1846+ {
1847+ using var sr = new StreamReader ( OpenRead ( path ) , encoding ) ;
1848+
1849+ string ? line ;
1850+
1851+ while ( ( line = sr . ReadLine ( ) ) != null )
1852+ {
1853+ yield return line ;
1854+ }
1855+ }
18501856 }
18511857
18521858 /// <summary>
0 commit comments