Using the Console Class
When you are creating a console application, the Console class in the System namespace contains several shared properties and methods that facilitate working with the standard input and output device, generally the keyboard and console window. Table 13.1 lists the members of the Console class.
Table 13.1. Members of the System.Console Class
Notice that the members of the Console class are all shared. The console generally consists of the keyboard and display screen (although other devices can be assigned as the standard input and output devices), and for practical purposes, there is only one console. Consequently, the Console class behaves like a Singleton; that is, because there is only one console and the console does not maintain any state, all of the members are shared.
Reading from and Writing to the Standard Input/Output Devices
The easiest way to implement line-based input and output from a console application is to use the Console.ReadLine method for input and Console.WriteLine for output.
By default, ReadLine returns a line of text from the keyboard after the user presses the Enter key, and WriteLine adds a carriage -return/ linefeed pair after the line of text sent to the display device. Listing 13.4 is a console application in Console.sln on this book's Web site that provides you with some console code to experiment with that demonstrates reading and writing to the standard devices.
Listing 13.4 Command-line sorting
1: Imports System.Threading 2: 3: Public Class Sort 4: 5: #Region " Public Methods " 6: Public Sub New(ByVal Args() As String) 7: Data = Args 8: End Sub 9: 10: Public Sub BubbleSort() 11: Dim I, J As Integer 12: 13: For I = Data.GetLowerBound(0) To Data.GetUpperBound(0) - 1 14: For J = I + 1 To Data.GetUpperBound(0) 15: If (Data(I) > Data(J)) Then 16: Dim Temp As Object 17: Temp = Data(I) 18: Data(I) = Data(J) 19: Data(J) = Temp 20: End If 21: Next 22: Next 23: End Sub 24: 25: Public Shared Sub Run() 26: Sort.DoCommand(GetInput().Split(", ".ToCharArray())) 27: End Sub 28: 29: Public Shared Sub DoCommand(ByVal Args() As String) 30: Dim Sorter As New Sort(Args) 31: Dim NewThread As New Thread(AddressOf Sorter.Process) 32: NewThread.Start() 33: End Sub 34: 35: #End Region 36: 37: #Region " Private Methods " 38: Private Data() As String 39: 40: Private Sub Process() 41: BubbleSort() 42: Display() 43: End Sub 44: 45: Private Sub Display() 46: Dim Enumerator As IEnumerator = Data.GetEnumerator 47: While Enumerator.MoveNext 48: System.Console.WriteLine(Enumerator.Current) 49: End While 50: End Sub 51: 52: Private Shared Sub Prompt() 53: Const Text As String = _ 54: "Enter comma delimited list to sort: " 55: System.Console.Write(Text) 56: End Sub 57: 58: Private Shared Function GetInput() As String 59: If (Command() = vbNullString) Then 60: Prompt() 61: Return System.Console.ReadLine() 62: Else 63: Return Command() 64: End If 65: End Function 66: #End Region 67: 68: End Class 69: 70: Module Module1 71: 72: Sub Main() 73: Sort.Run() 74: Thread.Sleep(2500) 75: End Sub 76: 77: End Module
The entire application is contained to Module1.vb. The module file contains a Module containing a very short startup Main procedure. Main invokes the shared method Sort. Run. The Public methods are defined in the Region between lines 5 and 35. There are only four public methods, making the classalso defined in the Module1.vb filerelatively easy to use. Run calls GetInput, which returns the command-line argument if there is one or prompts the user for input if the Command function returns a vbNullString. The returned input, whether from the command line or returned from the ReadLine method, is parsed using the String.Split method and passed to the shared method DoCommand. DoCommand constructs an instance of the Sort class and sorts the input on its own thread. After the BubbleSort is called, the string-sorted input is written to the standard output device using an IEnumerator and Console.WriteLine. The sample program demonstrates using ReadLine, WriteLine, and Write.
It is not necessary to write your own sort. System.Array implements a shared Sort method. Pass an array to the System.Array.Sort shared method, replacing the call to BubbleSort, to sort the array of strings referenced by Data in Listing 13.4.
A final note on the code in Listing 13.4. When shared members of the Console class were used, notice that the System namespace was prefixed to the class name Console. This was done to avoid ambiguity between the namespace given to the sample application when it was created as Console.sln and the Console class in the System namespace. You can use the fully qualified namespace statement to clarify references to classes with the same name residing in different namespaces.
Writing to the Error Device
The Error device (see Table 13.1) by default is the same device as the default Out device, the display adapter. If you use the Console.Error shared property to differentiate writing output versus writing error messages, you can elect to change the default Out or Error device. Listing 13.5 demonstrates a revision to Console.sln that writes an error message to the standard error device.
Listing 13.5 Writing an error message to the Error device
1: Private Shared Sub Validate(ByVal Input As String) 2: 3: Const Text As String = "Syntax: console 1,2,3,4,...,n" 4: 5: If (Input = vbNullString) Then 6: System.Console.Error.WriteLine(Text) 7: End 8: End If 9: 10: End Sub 11: 12: Private Shared Function GetInput() As String 13: If (Command() = vbNullString) Then 14: Prompt() 15: GetInput = System.Console.ReadLine() 16: Else 17: GetInput = Command() 18: End If 19: 20: Validate(GetInput) 21: Return GetInput 22: End Function
Use the function name if you really need a temporary variable, for example, for validation.
The revised code performs validation on the input. If Validate determines that Input is a vbNullString, the Error propertya TextWriter objectis used to write a syntax example to the error device and the application is terminated . Validate is defined on lines 1 through 10.
If you need a temporary variable to hold a calculated return value, as demonstrated in GetInput, you can use the function name as a temporary instead of introducing a new temporary. The function will not return until the end of the procedure or execution reaches a Return statement.
Changing the Error Device
The Console.Error property is a TextWriter object. By assigning an instance of a System.IO.TextWriter to the Error property, you can redirect error output from the default error device, the console, to an alternate instance of a TextWriter. (TextWriter is an abstract class, so you must choose a subclass of TextWriter.) Listing 13.6 demonstrates assigning the Error property to an instance of the System.IO.StreamWriter class that encapsulates a file stream. As a result, error output is written to a file rather than the console, allowing the behavior of your code to change without changing your code.
Listing 13.6 Redirecting error output to a file stream, System.IO.StreamWriter
1: Module Module1 2: 3: Private NewError As System.IO.StreamWriter 4: 5: Sub SetErrorDevice() 6: NewError = New System.IO.StreamWriter("error.log") 7: NewError.AutoFlush = True 8: System.Console.SetError(NewError) 9: End Sub 10: 11: Sub Main() 12: SetErrorDevice() 13: Sort.Run() 14: Thread.Sleep(2500) 15: End Sub 16: 17: End Module
One requirement necessary to allow Console redirection is to have UnmanagedCode permissions. Refer to Chapter 12, "Defining Attributes," for more on security permissions and attributes.
Revising Listing 13.4's Module1, we add a procedure called SetErrorDevice. The new procedure creates an instance of the StreamWriter class and calls the constructor with a suitable filename. AutoFlush is set to True to ensure that the stream is automatically flushed, that is, written to the file. If you forget to set AutoFlush to True, the file will be created on line 6, but there may be no data written to disk.
The Console.Error property is ReadOnly, so we must use the SetError method to intentionally redirect the Error device output to an alternate stream. (If you want to redirect the Console.In, Console.Out, and Console.Error devices to an alternate device, you have to explicitly call SetIn, SetOut, and SetError. This explicit method call was probably designed intentionally to avoid accidental console redirection.)