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 ;
@@ -1712,7 +1713,7 @@ public byte[] ReadAllBytes(string path)
17121713 using ( var stream = OpenRead ( path ) )
17131714 {
17141715 var buffer = new byte [ stream . Length ] ;
1715- _ = stream . Read ( buffer , 0 , buffer . Length ) ;
1716+ stream . ReadExactly ( buffer , 0 , buffer . Length ) ;
17161717 return buffer ;
17171718 }
17181719 }
@@ -1745,23 +1746,7 @@ public string[] ReadAllLines(string path)
17451746 /// <exception cref="ObjectDisposedException">The method was called after the client was disposed.</exception>
17461747 public string [ ] ReadAllLines ( string path , Encoding encoding )
17471748 {
1748- /*
1749- * We use the default buffer size for StreamReader - which is 1024 bytes - and the configured buffer size
1750- * for the SftpFileStream. We may want to revisit this later.
1751- */
1752-
1753- var lines = new List < string > ( ) ;
1754-
1755- using ( var stream = new StreamReader ( OpenRead ( path ) , encoding ) )
1756- {
1757- string ? line ;
1758- while ( ( line = stream . ReadLine ( ) ) != null )
1759- {
1760- lines . Add ( line ) ;
1761- }
1762- }
1763-
1764- return lines . ToArray ( ) ;
1749+ return ReadLines ( path , encoding ) . ToArray ( ) ;
17651750 }
17661751
17671752 /// <summary>
@@ -1792,15 +1777,8 @@ public string ReadAllText(string path)
17921777 /// <exception cref="ObjectDisposedException">The method was called after the client was disposed.</exception>
17931778 public string ReadAllText ( string path , Encoding encoding )
17941779 {
1795- /*
1796- * We use the default buffer size for StreamReader - which is 1024 bytes - and the configured buffer size
1797- * for the SftpFileStream. We may want to revisit this later.
1798- */
1799-
1800- using ( var stream = new StreamReader ( OpenRead ( path ) , encoding ) )
1801- {
1802- return stream . ReadToEnd ( ) ;
1803- }
1780+ using var sr = new StreamReader ( OpenRead ( path ) , encoding ) ;
1781+ return sr . ReadToEnd ( ) ;
18041782 }
18051783
18061784 /// <summary>
@@ -1815,7 +1793,7 @@ public string ReadAllText(string path, Encoding encoding)
18151793 /// <exception cref="ObjectDisposedException">The method was called after the client was disposed.</exception>
18161794 public IEnumerable < string > ReadLines ( string path )
18171795 {
1818- return ReadAllLines ( path ) ;
1796+ return ReadLines ( path , Encoding . UTF8 ) ;
18191797 }
18201798
18211799 /// <summary>
@@ -1831,7 +1809,30 @@ public IEnumerable<string> ReadLines(string path)
18311809 /// <exception cref="ObjectDisposedException">The method was called after the client was disposed.</exception>
18321810 public IEnumerable < string > ReadLines ( string path , Encoding encoding )
18331811 {
1834- return ReadAllLines ( path , encoding ) ;
1812+ // We open the file eagerly i.e. outside of the state machine created by yield,
1813+ // in order to a) throw resulting (e.g. file-related) exceptions eagerly; and b)
1814+ // to match what File.ReadLines does.
1815+ // This probably makes it behave more predictably/closer to what most people expect.
1816+ // The downside is that if the return value is never enumerated, the file
1817+ // is never closed (we can't do "using" here because it would be disposed
1818+ // as soon as we return). This conundrum also exists with File.ReadLines.
1819+
1820+ var sr = new StreamReader ( OpenRead ( path ) , encoding ) ;
1821+
1822+ return Enumerate ( sr ) ;
1823+
1824+ static IEnumerable < string > Enumerate ( StreamReader sr )
1825+ {
1826+ using ( sr )
1827+ {
1828+ string ? line ;
1829+
1830+ while ( ( line = sr . ReadLine ( ) ) != null )
1831+ {
1832+ yield return line ;
1833+ }
1834+ }
1835+ }
18351836 }
18361837
18371838 /// <summary>
0 commit comments