Hello, World in C# (Object-Oriented Version)

We begin with the traditional “Hello, world” program made popular beginning at least with the first edition of The C Programming Language by Kernighan and Ritchie. We already presented a version of this program in C# in Chapter 9. In this section we provide an object-oriented version and also introduce building components from C# that can be called from PerlNET. Along the way we cover basic input/output (I/O) and arrays.

The Hello Class

It has been suggested that the traditional “Hello, world” program is harmful to students.[1] They start thinking in terms of a monolithic program, and this habit is hard to shake. As Perl programmers, we can have the same problem ourselves, as Perl makes it so easy to create sophisticated scripts as monolithic code. In learning C# and .NET, which are so thoroughly object-oriented, it will be very helpful to us to start “thinking in objects” from the beginning. If you already have experience with an object-oriented language such as C++ or Java, you can breeze through this first section, paying attention only to the syntax of C#.

[1] Ralph Westfall, “Hello, World Considered Harmful,” Communications of the ACM, October, 2001, p. 129.

We begin with an object-oriented version that supports encapsulation. The implementation of an abstraction should be hidden from the rest of the system, or encapsulated, as shown in Figure B-1.

Figure B-1. An abstraction presents a public interface to a private implementation.


We define a class Hello with a public interface supporting

  • The method SayHello()

  • The property Greeting

There is a private implementation:

  • The string m_greeting

The C# code for this class is contained in the file Hello.cs in the folder HelloStep1 in the folder AppB that is provided for this appendix.


// Hello.cs

using System;

public class Hello
{
   private string m_greeting = "Hello, world";
   public Hello()
   {
   }
   public Hello(string greeting)
   {
      m_greeting = greeting;
   }
   public void SayHello()
   {
      Console.WriteLine(m_greeting);
   }
   public string Greeting
   {
      get
      {
         return m_greeting;
      }
      set
      {
         m_greeting = value;
      }
   }
}

A test program for this class is provided in the file TestHello.cs in the same folder.

// TestHello.cs

public class TestHello
{
   public static int Main(string[] args)
   {
      Hello obj;
      obj = new Hello();
      obj.SayHello();
      obj = new Hello("Goodbye, world");
      obj.SayHello();
      obj.Greeting = "Brave New World";
      System.Console.WriteLine(obj.Greeting);
      return 0;
   }
}

A Visual Studio solution is provided, which you can build as described in Appendix A, and run (use menu Debug | Start Without Debugging or keyboard Ctrl + F5). Alternatively, you can run the batch file build.bat at the command line and run the program by simply typing the name of the EXE file Test-Hello.exe. You will see the following output:

Hello, world
Goodbye, world
Brave New World

Classes and Objects

The most basic concept in this program is that of class, which represents a type of data. A class can be thought of as a template from which individual instances can be created. An instance of a class is called an object.

In C# a class is defined using the keyword class. A C# class can contain several kinds of elements. The Hello class includes the following elements:

  • The private data member m_greeting. Each object instance will have its own value for this data member. A default value of “Hello, world” is defined.

  • Constructors, which can be used to provide initialization at the time an object instance is created. This class has two constructors. A constructor has the same name as the class and can take a parameter list. The first constructor has no parameters and is called the default constructor. The second constructor takes a string parameter, which is used to assign a nondefault value to m_greeting.

  • The method SayHello, which is used to implement behavior, in this case printing out a greeting message.

  • The property Greeting, which exposes data outside the class.

In C# an object instance is created using the new keyword, as illustrated in the TestHello.cs program.

obj = new Hello();
...
obj = new Hello("Goodbye, world");

The first line invokes the default constructor, and the second line invokes the constructor with a string parameter.

Access Control

C# provides language features so that the programmer can enforce access rules for a class and its members. (Not all languages that provide classes and objects have support for access control. For example, Perl expects programmers to play by the rules, but does not have an enforcement mechanism.)

C# has two means for controlling accessibility of class members. Access can be controlled at both the class level and the member level.

CLASS ACCESSIBILITY

An access modifier can be placed in front of the class keyword to control who can get at the class at all. Access can be further restricted by member accessibility, discussed in the next subsection.

  • The most common access modifier of a class is public, which makes the class available to everyone.

  • The internal modifier makes a class available within the current assembly, which can be thought of as a logical EXE or DLL. (We discuss assemblies later in this appendix.) If you leave out an access modifier in front of a class, internal will be assumed.

MEMBER ACCESSIBILITY

Access to individual class members can be controlled by placing an access modifier such as public or private in front of the member. Member access can only further restrict access to a class, not widen it. Thus if you have a class with internal accessibility, making a member public will not make it accessible from outside the assembly.

  • A public member can be accessed from outside the class.

  • A private member can be accessed only from within the class (but not from derived classes).

  • Inheritance introduces a third kind of accessibility, protected. A protected member can be accessed from within the class and from within any derived classes. (We discuss inheritance in C# later in this appendix.)

  • An internal member can be accessed from within classes in the same assembly but not from classes outside the assembly.

  • An internal protected member can be accessed from within the assembly and from outside the assembly by a derived class.

Methods

Typically, a class will specify behavior as well as data. A class encapsulates data and behavior in a single entity. A method specifies the behavior and consists of

  • An access specifier, typically public or private

  • A return type (can be void if the method does not return data)

  • A method name, which can be any legal C# identifier

  • A parameter list, enclosed by parentheses, which specifies data that is passed to the method (can be empty if no data is passed)

  • A method body, enclosed by curly braces, which contains the C# code that the method will execute

public void SayHello()
{
      Console.WriteLine(m_greeting);
}

Fields

In C# data members of a class are referred to as fields. Fields may be assigned access modifiers like other members of a class. In the Step1 version of the Hello example, the greeting data is stored in a private field m_greeting. Although not usually recommended, you can also have public fields, as illustrated in HelloStep1F.

// Hello.cs

using System;

public class Hello
{
   public string Greeting = "Hello, world";
   public Hello()
   {
   }
   public Hello(string greeting)
   {
      Greeting = greeting;
   }
   public void SayHello()
   {
      Console.WriteLine(Greeting);
   }
}

This version of the Hello class presents exactly the same interface to the outside world as the original version, so the test program does not have to be changed. Notice that variables in C# are case-sensitive, so we may have both the data field Greeting and the parameter greeting.

Properties

Although exposing internal data of a class to the outside world is generally a bad idea, the data interface is rather convenient for users of the class. Thus in the test program we can read and write the data by simple assignments, without having to invoke any special methods.

...
obj.Greeting = "Brave New World";
System.Console.WriteLine(obj.Greeting);

It is easy to understand how this notation works with the Step1F version, where there is a public data member Greeting. But it also works in the Step1 version, where a property is defined, using the following C# code. Note that in this case the property is read-write (both get and set are provided). It is also possible to implement read-only (just get) and write-only (just set) properties.

public string Greeting
{
   get
   {
      return m_greeting;
   }
   set
   {
      m_greeting = value;
   }
}

Here value is a special C# keyword that is used for implementing properties. There are several advantages to using properties in place of public fields. The basic advantage is encapsulation. We can change the data representation without breaking the rest of the program, and client programs cannot directly access private data, possibly causing damage. Another advantage is that code can be provided as part of the data access. For example, in a read-property the value could be calculated or obtained from some real-time sensor and not actually stored as data at all. In a write-property some validation could be performed before actually making the assignment.

System.Console

The System.Console class provides convenient methods for input and output. The Hello example illustrates only one of these methods, WriteLine, for performing output. It writes a string followed by a new line.

System.Console.WriteLine(obj.Greeting);

In later programs we will see illustrations of other methods, such as Write, which writes a string without the new line, and ReadLine, which reads a string from the console.

Namespaces

Much standard functionality in C# is provided through many classes in the .NET Framework. Related classes are grouped into namespaces. Many useful classes, such as Console, are in the System namespace. The fully qualified name of a class is specified by the namespace followed by a dot followed by a class name.

System.Console

A using statement allows a class to be referred to by its class name alone. Whereas in the TestHello class we use the fully qualified name, in the Hello class we rely on the using statement.

// Hello.cs

using System;

public class Hello
{
   ...
   public void SayHello()
   {
      Console.WriteLine(m_greeting);
   }
   ...

Main Method

The TestHello class has a distinguished method called Main, which is the entry point to the program. It is a static method, which means that no object instance of the TestHello class need be created in order to call the method. The Main method is called by the runtime environment, and the method may return an integer code when it exits. (Alternatively, the method may be declared as void, with no return value.) The Main method has an array of strings as an argument, which will receive the command line arguments from invoking the program.

// TestHello.cs

public class TestHello
{
   public static int Main(string[] args)
   {
      Hello obj;
      obj = new Hello();
      obj.SayHello();
      ...

Every method in C# has one or more statements. A statement is terminated by a semicolon. A statement may be spread out over several lines. Variables in C# must be declared.

Hello obj;

Unlike in C++, this statement does not cause a Hello object to be constructed; only a reference is created. The following line instantiates the object via the new operator, and then a method is invoked on the object.

obj = new Hello();
obj.SayHello();

Command-Line Arguments

The Hello program does not do anything with command-line arguments. The ShowArgs program illustrates retrieving the command-line arguments. It shows them on one line, separated by commas. It also maintains a count of the total number of characters in these arguments. Note the syntax for working with arrays, and the Length property. The for loop is also like its counterpart in C.

// ShowArgs.cs

using System;

class ShowArgs
{
   static void Main(string[] args)
   {
      int numChars = 0;
      for (int i = 0; i < args.Length; i++)
      {
         numChars += args[i].Length;
         Console.Write("{0}, ", args[i]);
      }
      Console.WriteLine();
      Console.WriteLine("{0} arguments", args.Length);
      Console.WriteLine("{0} characters", numChars);
   }
}

If you run the program at the command line, you can simply enter the arguments when you invoke the program. Here is a sample run.

>showargs one two three
one, two, three,
3 arguments
11 characters

If you are building the project in Visual Studio, you can set the command-line arguments using project properties. In Solution Explorer right-click on the project and choose Properties from the context menu. Choose Debugging from the Configuration Properties. You can then set command-line arguments under the Start Options, as illustrated in Figure B-2.

Figure B-2. Setting command-line arguments in Visual Studio.


..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.222.196.175