You know the state of the object in advance (usually because your code has instantiated it), but the object is mis-configured. The following example illustrates this issue. It creates a read-only FileStream object and then attempts to write to it.
using System;
using System.IO;
using System.Text;
using System.Threading.Tasks;
public class Example
{
public static async Task Main()
{
Encoding enc = Encoding.Unicode;
String value = "This is a string to persist.";
Byte[] bytes = enc.GetBytes(value);
FileStream fs = new FileStream(@".\TestFile.dat",
FileMode.Open,
FileAccess.Read);
Task t = fs.WriteAsync(enc.GetPreamble(), 0, enc.GetPreamble().Length);
Task t2 = t.ContinueWith((a) => fs.WriteAsync(bytes, 0, bytes.Length));
await t2;
fs.Close();
}
}
// The example displays the following output:
// Unhandled Exception: System.NotSupportedException: Stream does not support writing.
// at System.IO.Stream.BeginWriteInternal(Byte[] buffer, Int32 offset, Int32 count, AsyncCallback callback, Object state
// , Boolean serializeAsynchronously)
// at System.IO.FileStream.BeginWrite(Byte[] array, Int32 offset, Int32 numBytes, AsyncCallback userCallback, Object sta
// teObject)
// at System.IO.Stream.<>c.<BeginEndWriteAsync>b__53_0(Stream stream, ReadWriteParameters args, AsyncCallback callback,
// Object state)
// at System.Threading.Tasks.TaskFactory`1.FromAsyncTrim[TInstance,TArgs](TInstance thisRef, TArgs args, Func`5 beginMet
// hod, Func`3 endMethod)
// at System.IO.Stream.BeginEndWriteAsync(Byte[] buffer, Int32 offset, Int32 count)
// at System.IO.FileStream.WriteAsync(Byte[] buffer, Int32 offset, Int32 count, CancellationToken cancellationToken)
// at System.IO.Stream.WriteAsync(Byte[] buffer, Int32 offset, Int32 count)
// at Example.Main()
open System.IO
open System.Text
let main = task {
let enc = Encoding.Unicode
let value = "This is a string to persist."
let bytes = enc.GetBytes value
let fs = new FileStream(@".\TestFile.dat", FileMode.Open, FileAccess.Read)
let t = fs.WriteAsync(enc.GetPreamble(), 0, enc.GetPreamble().Length)
let t2 = t.ContinueWith(fun a -> fs.WriteAsync(bytes, 0, bytes.Length))
let! _ = t2
fs.Close()
}
main.Wait()
// The example displays the following output:
// Unhandled Exception: System.NotSupportedException: Stream does not support writing.
// at System.IO.Stream.BeginWriteInternal(Byte[] buffer, Int32 offset, Int32 count, AsyncCallback callback, Object state
// , Boolean serializeAsynchronously)
// at System.IO.FileStream.BeginWrite(Byte[] array, Int32 offset, Int32 numBytes, AsyncCallback userCallback, Object sta
// teObject)
// at System.IO.Stream.<>c.<BeginEndWriteAsync>b__53_0(Stream stream, ReadWriteParameters args, AsyncCallback callback,
// Object state)
// at System.Threading.Tasks.TaskFactory`1.FromAsyncTrim[TInstance,TArgs](TInstance thisRef, TArgs args, Func`5 beginMet
// hod, Func`3 endMethod)
// at System.IO.Stream.BeginEndWriteAsync(Byte[] buffer, Int32 offset, Int32 count)
// at System.IO.FileStream.WriteAsync(Byte[] buffer, Int32 offset, Int32 count, CancellationToken cancellationToken)
// at System.IO.Stream.WriteAsync(Byte[] buffer, Int32 offset, Int32 count)
// at <StartupCode:fs>.main()
Imports System.IO
Imports System.Text
Imports System.Threading.Tasks
Module Example
Public Sub Main()
Dim enc As Encoding = Encoding.Unicode
Dim value As String = "This is a string to persist."
Dim bytes() As Byte = enc.GetBytes(value)
Dim fs As New FileStream(".\TestFile.dat",
FileMode.Open,
FileAccess.Read)
Dim t As Task = fs.WriteAsync(enc.GetPreamble(), 0, enc.GetPreamble().Length)
Dim t2 As Task = t.ContinueWith(Sub(a) fs.WriteAsync(bytes, 0, bytes.Length))
t2.Wait()
fs.Close()
End Sub
End Module
' The example displays the following output:
' Unhandled Exception: System.NotSupportedException: Stream does not support writing.
' at System.IO.Stream.BeginWriteInternal(Byte[] buffer, Int32 offset, Int32 count, AsyncCallback callback, Object state
' , Boolean serializeAsynchronously)
' at System.IO.FileStream.BeginWrite(Byte[] array, Int32 offset, Int32 numBytes, AsyncCallback userCallback, Object sta
' teObject)
' at System.IO.Stream.<>c.<BeginEndWriteAsync>b__53_0(Stream stream, ReadWriteParameters args, AsyncCallback callback,
' Object state)
' at System.Threading.Tasks.TaskFactory`1.FromAsyncTrim[TInstance,TArgs](TInstance thisRef, TArgs args, Func`5 beginMet
' hod, Func`3 endMethod)
' at System.IO.Stream.BeginEndWriteAsync(Byte[] buffer, Int32 offset, Int32 count)
' at System.IO.FileStream.WriteAsync(Byte[] buffer, Int32 offset, Int32 count, CancellationToken cancellationToken)
' at System.IO.Stream.WriteAsync(Byte[] buffer, Int32 offset, Int32 count)
' at Example.Main()
Ycan eliminate the exception by ensuring that the instantiated object supports the functionality you intend. The following example addresses the problem of the read-only FileStream object by providing the correct arguments to the FileStream.FileStream(String, FileMode, FileAccess) constructor.
You don't know the state of the object in advance, and the object doesn't support a particular operation. In most cases, the object should include a property or method that indicates whether it supports a particular set of operations. You can eliminate the exception by checking the value of the object and invoking the member only if appropriate.
The following example defines a DetectEncoding
method that throws a NotSupportedException exception when it attempts to read from the beginning of a stream that does not support read access.
using System;
using System.IO;
using System.Threading.Tasks;
public class TestPropEx1
{
public static async Task Main()
{
String name = @".\TestFile.dat";
var fs = new FileStream(name,
FileMode.Create,
FileAccess.Write);
Console.WriteLine("Filename: {0}, Encoding: {1}",
name, await FileUtilities1.GetEncodingType(fs));
}
}
public class FileUtilities1
{
public enum EncodingType
{ None = 0, Unknown = -1, Utf8 = 1, Utf16 = 2, Utf32 = 3 }
public async static Task<EncodingType> GetEncodingType(FileStream fs)
{
Byte[] bytes = new Byte[4];
int bytesRead = await fs.ReadAsync(bytes, 0, 4);
if (bytesRead < 2)
return EncodingType.None;
if (bytesRead >= 3 & (bytes[0] == 0xEF && bytes[1] == 0xBB && bytes[2] == 0xBF))
return EncodingType.Utf8;
if (bytesRead == 4)
{
var value = BitConverter.ToUInt32(bytes, 0);
if (value == 0x0000FEFF | value == 0xFEFF0000)
return EncodingType.Utf32;
}
var value16 = BitConverter.ToUInt16(bytes, 0);
if (value16 == (ushort)0xFEFF | value16 == (ushort)0xFFFE)
return EncodingType.Utf16;
return EncodingType.Unknown;
}
}
// The example displays the following output:
// Unhandled Exception: System.NotSupportedException: Stream does not support reading.
// at System.IO.FileStream.BeginRead(Byte[] array, Int32 offset, Int32 numBytes, AsyncCallback callback, Object state)
// at System.IO.Stream.<>c.<BeginEndReadAsync>b__46_0(Stream stream, ReadWriteParameters args, AsyncCallback callback, Object state)
// at System.Threading.Tasks.TaskFactory`1.FromAsyncTrim[TInstance, TArgs](TInstance thisRef, TArgs args, Func`5 beginMethod, Func`3 endMethod)
// at System.IO.Stream.BeginEndReadAsync(Byte[] buffer, Int32 offset, Int32 count)
// at System.IO.FileStream.ReadAsync(Byte[] buffer, Int32 offset, Int32 count, CancellationToken cancellationToken)
// at System.IO.Stream.ReadAsync(Byte[] buffer, Int32 offset, Int32 count)
// at FileUtilities.GetEncodingType(FileStream fs) in C:\Work\docs\program.cs:line 26
// at Example.Main() in C:\Work\docs\program.cs:line 13
// at Example.<Main>()
open System
open System.IO
module FileUtilities =
type EncodingType =
| None = 0
| Unknown = -1
| Utf8 = 1
| Utf16 = 2
| Utf32 = 3
let getEncodingType (fs: FileStream) =
task {
let bytes = Array.zeroCreate<byte> 4
let! bytesRead = fs.ReadAsync(bytes, 0, 4)
if bytesRead < 2 then
return EncodingType.None
elif bytesRead >= 3 && bytes[0] = 0xEFuy && bytes[1] = 0xBBuy && bytes[2] = 0xBFuy then
return EncodingType.Utf8
else
let value = BitConverter.ToUInt32(bytes, 0)
if bytesRead = 4 && (value = 0x0000FEFFu || value = 0xFEFF0000u) then
return EncodingType.Utf32
else
let value16 = BitConverter.ToUInt16(bytes, 0)
if value16 = 0xFEFFus || value16 = 0xFFFEus then
return EncodingType.Utf16
else
return EncodingType.Unknown
}
let main _ =
task {
let name = @".\TestFile.dat"
let fs = new FileStream(name, FileMode.Create, FileAccess.Write)
let! et = FileUtilities.getEncodingType fs
printfn $"Filename: {name}, Encoding: {et}"
}
// The example displays the following output:
// Unhandled Exception: System.NotSupportedException: Stream does not support reading.
// at System.IO.FileStream.BeginRead(Byte[] array, Int32 offset, Int32 numBytes, AsyncCallback callback, Object state)
// at System.IO.Stream.<>c.<BeginEndReadAsync>b__46_0(Stream stream, ReadWriteParameters args, AsyncCallback callback, Object state)
// at System.Threading.Tasks.TaskFactory`1.FromAsyncTrim[TInstance, TArgs](TInstance thisRef, TArgs args, Func`5 beginMethod, Func`3 endMethod)
// at System.IO.Stream.BeginEndReadAsync(Byte[] buffer, Int32 offset, Int32 count)
// at System.IO.FileStream.ReadAsync(Byte[] buffer, Int32 offset, Int32 count, CancellationToken cancellationToken)
// at System.IO.Stream.ReadAsync(Byte[] buffer, Int32 offset, Int32 count)
// at FileUtilities.GetEncodingType(FileStream fs)
// at Example.Main()
// at Example.<Main>()
Imports System.IO
Imports System.Threading.Tasks
Module Example2
Public Sub Main()
Dim name As String = ".\TestFile.dat"
Dim fs As New FileStream(name,
FileMode.Create,
FileAccess.Write)
Console.WriteLine("Filename: {0}, Encoding: {1}",
name, FileUtilities2.GetEncodingType(fs))
End Sub
End Module
Public Class FileUtilities2
Public Enum EncodingType As Integer
None = 0
Unknown = -1
Utf8 = 1
Utf16 = 2
Utf32 = 3
End Enum
Public Shared Function GetEncodingType(fs As FileStream) As EncodingType
Dim bytes(3) As Byte
Dim t As Task(Of Integer) = fs.ReadAsync(bytes, 0, 4)
t.Wait()
Dim bytesRead As Integer = t.Result
If bytesRead < 2 Then Return EncodingType.None
If bytesRead >= 3 And (bytes(0) = &HEF AndAlso bytes(1) = &HBB AndAlso bytes(2) = &HBF) Then
Return EncodingType.Utf8
End If
If bytesRead = 4 Then
Dim value As UInteger = BitConverter.ToUInt32(bytes, 0)
If value = &HFEFF Or value = &HFEFF0000 Then
Return EncodingType.Utf32
End If
End If
Dim value16 As UInt16 = BitConverter.ToUInt16(bytes, 0)
If value16 = &HFEFF Or value16 = &HFFFE Then
Return EncodingType.Utf16
End If
Return EncodingType.Unknown
End Function
End Class
' The example displays the following output:
' Unhandled Exception: System.NotSupportedException: Stream does not support reading.
' at System.IO.Stream.BeginReadInternal(Byte[] buffer, Int32 offset, Int32 count, AsyncCallback callback, Object state,
' Boolean serializeAsynchronously)
' at System.IO.FileStream.BeginRead(Byte[] array, Int32 offset, Int32 numBytes, AsyncCallback userCallback, Object stat
' eObject)
' at System.IO.Stream.<>c.<BeginEndReadAsync>b__43_0(Stream stream, ReadWriteParameters args, AsyncCallback callback, O
' bject state)
' at System.Threading.Tasks.TaskFactory`1.FromAsyncTrim[TInstance,TArgs](TInstance thisRef, TArgs args, Func`5 beginMet
' hod, Func`3 endMethod)
' at System.IO.Stream.BeginEndReadAsync(Byte[] buffer, Int32 offset, Int32 count)
' at System.IO.FileStream.ReadAsync(Byte[] buffer, Int32 offset, Int32 count, CancellationToken cancellationToken)
' at System.IO.Stream.ReadAsync(Byte[] buffer, Int32 offset, Int32 count)
' at FileUtilities2.GetEncodingType(FileStream fs)
' at Example.Main()
You can eliminate the exception by examining the value of the FileStream.CanRead property and exiting the method if the stream is read-only.
public static async Task<EncodingType> GetEncodingType(FileStream fs)
{
if (!fs.CanRead)
return EncodingType.Unknown;
Byte[] bytes = new Byte[4];
int bytesRead = await fs.ReadAsync(bytes, 0, 4);
if (bytesRead < 2)
return EncodingType.None;
if (bytesRead >= 3 & (bytes[0] == 0xEF && bytes[1] == 0xBB && bytes[2] == 0xBF))
return EncodingType.Utf8;
if (bytesRead == 4)
{
var value = BitConverter.ToUInt32(bytes, 0);
if (value == 0x0000FEFF | value == 0xFEFF0000)
return EncodingType.Utf32;
}
var value16 = BitConverter.ToUInt16(bytes, 0);
if (value16 == (ushort)0xFEFF | value16 == (ushort)0xFFFE)
return EncodingType.Utf16;
return EncodingType.Unknown;
}
}
// The example displays the following output:
// Filename: .\TestFile.dat, Encoding: Unknown
let getEncodingType (fs: FileStream) =
task {
if not fs.CanRead then
return EncodingType.Unknown
else
let bytes = Array.zeroCreate<byte> 4
let! bytesRead = fs.ReadAsync(bytes, 0, 4)
if bytesRead < 2 then
return EncodingType.None
elif bytesRead >= 3 && bytes[0] = 0xEFuy && bytes[1] = 0xBBuy && bytes[2] = 0xBFuy then
return EncodingType.Utf8
else
let value = BitConverter.ToUInt32(bytes, 0)
if bytesRead = 4 && (value = 0x0000FEFFu || value = 0xFEFF0000u) then
return EncodingType.Utf32
else
let value16 = BitConverter.ToUInt16(bytes, 0)
if value16 = 0xFEFFus || value16 = 0xFFFEus then
return EncodingType.Utf16
else
return EncodingType.Unknown
}
// The example displays the following output:
// Filename: .\TestFile.dat, Encoding: Unknown
Public Class FileUtilities3
Public Enum EncodingType As Integer
None = 0
Unknown = -1
Utf8 = 1
Utf16 = 2
Utf32 = 3
End Enum
Public Shared Function GetEncodingType(fs As FileStream) As EncodingType
If Not fs.CanRead Then
Return EncodingType.Unknown
End If
Dim bytes(3) As Byte
Dim t As Task(Of Integer) = fs.ReadAsync(bytes, 0, 4)
t.Wait()
Dim bytesRead As Integer = t.Result
If bytesRead < 2 Then Return EncodingType.None
If bytesRead >= 3 And (bytes(0) = &HEF AndAlso bytes(1) = &HBB AndAlso bytes(2) = &HBF) Then
Return EncodingType.Utf8
End If
If bytesRead = 4 Then
Dim value As UInteger = BitConverter.ToUInt32(bytes, 0)
If value = &HFEFF Or value = &HFEFF0000 Then
Return EncodingType.Utf32
End If
End If
Dim value16 As UInt16 = BitConverter.ToUInt16(bytes, 0)
If value16 = &HFEFF Or value16 = &HFFFE Then
Return EncodingType.Utf16
End If
Return EncodingType.Unknown
End Function
End Class
' The example displays the following output:
' Filename: .\TestFile.dat, Encoding: Unknown