Update behaviors of new TextWriter StringBuilder overloads (dotnet/coreclr#18578)
authorStephen Toub <stoub@microsoft.com>
Fri, 22 Jun 2018 01:36:59 +0000 (21:36 -0400)
committerGitHub <noreply@github.com>
Fri, 22 Jun 2018 01:36:59 +0000 (21:36 -0400)
Two issues addressed:
- CancellationToken was being ignored; the overloads should check whether cancellation was requested.  This applies to the existing Write{Line}Async overloads that were added in 2.1.
- The other overloads support inputs (string, char[], etc.) being null, and just treat that the same as an empty string.  We should do the same for StringBuilder rather than throwing.

Commit migrated from https://github.com/dotnet/coreclr/commit/538a51e593964ad34147e3143153885135412837

src/libraries/System.Private.CoreLib/src/System/IO/TextWriter.cs

index c41dabd..0acb4d1 100644 (file)
@@ -559,18 +559,17 @@ namespace System.IO
         /// <param name="value">The string (as a StringBuilder) to write to the stream</param>
         public virtual Task WriteAsync(StringBuilder value, CancellationToken cancellationToken = default)
         {
-            // Do the argument checking before 'going async' so you get it early
-            if (value == null)
-            {
-                throw new ArgumentNullException(nameof(value));
-            }
-            // Then do the rest which may be deferred (done in the returned Task)
-            return WriteAsyncCore(value, cancellationToken);
+            return
+                cancellationToken.IsCancellationRequested ? Task.FromCanceled(cancellationToken) :
+                value == null ? Task.CompletedTask :
+                WriteAsyncCore(value, cancellationToken);
 
             async Task WriteAsyncCore(StringBuilder sb, CancellationToken ct)
             {
                 foreach (ReadOnlyMemory<char> chunk in sb.GetChunks())
+                {
                     await WriteAsync(chunk, ct).ConfigureAwait(false);
+                }
             }
         }
 
@@ -596,6 +595,7 @@ namespace System.IO
         }
 
         public virtual Task WriteAsync(ReadOnlyMemory<char> buffer, CancellationToken cancellationToken = default) =>
+            cancellationToken.IsCancellationRequested ? Task.FromCanceled(cancellationToken) :
             MemoryMarshal.TryGetArray(buffer, out ArraySegment<char> array) ?
                 WriteAsync(array.Array, array.Offset, array.Count) :
                 Task.Factory.StartNew(state =>
@@ -631,10 +631,21 @@ namespace System.IO
         /// StringBuilder.GetChunks() method to avoid creating the intermediate string
         /// </summary>
         /// <param name="value">The string (as a StringBuilder) to write to the stream</param>
-        public async virtual Task WriteLineAsync(StringBuilder value, CancellationToken cancellationToken = default)
+        public virtual Task WriteLineAsync(StringBuilder value, CancellationToken cancellationToken = default)
         {
-            await WriteAsync(value, cancellationToken).ConfigureAwait(false);
-            await WriteAsync(CoreNewLine, cancellationToken).ConfigureAwait(false);
+            return
+                cancellationToken.IsCancellationRequested ? Task.FromCanceled(cancellationToken) :
+                value == null ? WriteAsync(CoreNewLine, cancellationToken) :
+                WriteLineAsyncCore(value, cancellationToken);
+
+            async Task WriteLineAsyncCore(StringBuilder sb, CancellationToken ct)
+            {
+                foreach (ReadOnlyMemory<char> chunk in sb.GetChunks())
+                {
+                    await WriteAsync(chunk, ct).ConfigureAwait(false);
+                }
+                await WriteAsync(CoreNewLine, ct).ConfigureAwait(false);
+            }
         }
 
         public Task WriteLineAsync(char[] buffer)
@@ -659,6 +670,7 @@ namespace System.IO
         }
 
         public virtual Task WriteLineAsync(ReadOnlyMemory<char> buffer, CancellationToken cancellationToken = default) =>
+            cancellationToken.IsCancellationRequested ? Task.FromCanceled(cancellationToken) :
             MemoryMarshal.TryGetArray(buffer, out ArraySegment<char> array) ?
                 WriteLineAsync(array.Array, array.Offset, array.Count) :
                 Task.Factory.StartNew(state =>