Classes


Conceptually, classes in both C# and Java are very similar. A class is the template for an object, which is a data type that can hold both data and functionality that acts upon that data. Instantiating an object means creating a specific occurrence of that object, based on the class template. C# classes contain members that include methods (including constructors) and fields, like Java classes. However, some important conceptual differences exist between C# and Java classes, and a few different keywords exist too (as you would expect).

Access Modifiers

As with Java, you can add the usual modifiers to the start of the class or member declaration to modify the behavior of the class or member. The following table lists the C# modifiers and their Java equivalents.

Access Modifier

Java Equivalent

Description

Public

public

No restrictions on access. Members of enum and interface, as well as namespaces, are public by default.

private

private

Accessible only to the declaring class. Members of class and struct are private by default.

internal

n/a

Accessible to files in the same assembly.

protected

n/a

Accessible to the declaring class, and any subclass of the declaring class. In C# protected is more restrictive than in Java. Protected access will not allow other files in the same assembly to access the member.

protected internal

protected

Accessible to assembly files and subclasses of declaring class.

The private keyword is used to make methods and variables accessible only from within the containing class. It serves the same function in both languages. The public modifier allows entities outside the package/namespace to access the members of the class. However, C# and Java differ in the way protected and default are handled. Whereas in Java, protected makes the method or variable accessible to classes in the same package or subclasses of the class, in C# protected makes code visible only to that class and to subclasses that inherit from it.

C# also introduces a new access modifier: internal. The internal keyword modifies data members so that they are visible to all code within the entire component but not clients of that component. The difference between the no modifier in Java (which signifies an element that is accessible only to elements within the package) and internal is that internal is accessible to all elements of the assembly, which can span multiple namespaces.

Class Members

As you have seen throughout this appendix, the differences in syntax between C# and Java when declaring and referring to classes and their members is minimal. However, there are marked differences in class member modifier syntax, as explained in the following table.

Member Modifiers

Java Equivalent

Description

virtual

n/a

Allows target members to be overridden by an inherited class (the default in Java).

Static

static

Target member marked as static belongs to class and not instance of class. Hence, there is no need to instantiate the class in order to gain access to it.

Event

n/a

Used to bind client code to events of the class, the event modifier allows you to spec- ify a delegate that will be called when some event in your code occurs. Note that it is the job of the class programmer to define when and where the event is raised, and the job of the subscriber to choose how to handle it.

abstract

abstract

Indicates that the target member is implicitly virtual and has no implementation code. The derived class must provide this implementa- tion and the implemented method must be marked as override.

Const

final

Indicates that the target member cannot be modified. Java also has a const keyword, which at the time of this writing is simply a reserved word.

readonly

n/a

Indicates that the target member can only be assigned values in its declaration or in the constructor of its containing class.

Extern

n/a

Indicates that the target member is imple- mented externally. This modifier is typically used with the DllImport attribute.

partial

n/a

Allows the class, struct, or interface to be defined in multiple files.

override

n/a

Indicates that the target member provides a new implementation of a member inherited from a base class.

For more information on delegates and events, refer to Chapter 6, "Delegates and Events."

As with Java, defining abstract methods in C# mandates that the class be abstract.

C# does not have a native modifier, and there is also no C# version of transient, volatile, or synchronized at the time of writing. In Java, using native indicates that the method is implemented in a platform- dependent language. It requires that the method be abstract because the implementation is to be found elsewhere. The closest relative to this type of functionality is the extern modifier. Using extern implies that the code is implemented externally (by some native DLL for example). Unlike Java, however, there is no need to use the abstract keyword in association with it. In the following snippet, the Flower class displays an example of how extern can be used:

 public class Flower { public Flower(){} public extern int GetColor(); // rest of Flower class definition } 

This doesn't make much sense without using the DllImport attribute to specify the external implementation. The following code provides the appropriate modifications, assuming there is a See() function exported by the User32.dll resource:

 public class Flower { public Flower(){} [System.Runtime.InteropServices.DllImport ("User32.dll")] public static extern int GetColor(); // rest of Flower class definition } 

Note that GetColor() is now marked as static. The DllImport attribute requires this of the methods it is used on.

Passing as reference to methods

Java and C# differ extensively in syntax and ideology regarding the way methods are handled by an object. For one thing, in C# not all reference data type parameters are passed as references and not all simple data types have to be passed by value. You have the option to pass arguments by value as an in parameter (this is the default way parameters are passed), by reference as a ref parameter, or as an out parameter. This is illustrated by the following code:

 public static void Main(string[] args) { int a = 10; Console.WriteLine(a); AddOne(a); Console.WriteLine(a); } public static void AddOne(int a) { a++; } 

This produces the following output in both C# and Java:

10 10 

Because a is passed by value, the value that is passed is not tied to the value a in Main(). Consequently, incrementing a in the Add() method does not affect a in Main(). This is probably not the behavior you want; you would like the changes made to a to be remembered after the method call. You can do this by passing by reference instead of by value, like this:

 public static void Main(string[] args) { int a = 10; Console.WriteLine(a); AddOne(ref a); Console.WriteLine(a); } public static void AddOne(ref int a) { a++; } 

This produces the following output:

10  11

So, to use a reference parameter, you precede the parameter type with the ref keyword. You can also pass values back from a method using the out parameter. Note that out parameters do not need to be initialized before they are passed as arguments. The following code displays 100:

 public static void Main(string[] args) { int a; Add(out a); Console.WriteLine(a); } public static void Add(out int a) { a = 100; } 

Properties

Unlike Java, C# does not use get and set methods to access an object's internal attributes. Instead it combines these methods together into another kind of class member called a property. A property contains a get accessor, which allows reading of internal fields of an object, and a set accessor that allows you to change the value of an internal field. The value keyword represents the new value to the right of the equals sign at assignment time. Not including the appropriate accessor in the property declaration will make the property either read-only (no set) or write-only (no get). The following class, Person, contains a few properties, called Age and Name:

 public class Person { private int age; private string name; public Person(string name) { this.name = name; } public int Age { get { return age; } set { age = value; } } public string Name { get { return name; } } } 

In this example, the property Age has a get and set accessor so you can read or write to the property. Name, however, is created only after you create a new instance of the properties object; after this you can only read the value of the Name property. Properties are accessed as if they are public fields:

 Person john = new Person("John Smith"); john.Age = 21; Console.WriteLine("My name is {0}, and I am {1} years old.", john.Name, john.Age); 

The output of this code is:

My name is John Smith, and I am 21 years old.

Note that property names must be unique.

Destructors

C# uses destructors in a similar way to C++. They work similarly to finalizers in Java; their syntax, however, is very different. With destructors, a tilde (~) prefixes the class name:

 ~Sample()  { } 

A word of advice concerning code in the destructor: The garbage collector in .NET is not invoked immediately after a variable goes out of scope. Indeed, there are certain intervals or memory conditions that bring the thread to life. Because there is a possibility that it might be triggered in low memory situations, consider making code in the destructor as short as possible. It is also a good idea to call close() on resource-intensive objects before destroying the controllers that use them.

Class Inheritance

Class inheritance in C# is also implemented in a very similar way to Java. Both languages are based on single implementation inheritance (in other words, a subclass is allowed to inherit only from one other class) and multiple interface inheritance (a class can implement as many interfaces as desired).

C# does not have Java's extends or implements modifiers. To derive from a class or implement an interface in C#, you use the : operator. When a class base list contains a base class and interfaces, the base class comes first in the list. The interface keyword is used to declare an interface. The following code shows examples of how to use these concepts:

 //declare a parent/base class class MyBaseClass { //class members } // declare an interface IFirstInterface interface IFirstInterface { // interface members } // declare a subclass of MyBaseClass that inherits from interfaces too class MySubClass : MyBaseClass, IFirstInterface, ISecondInterface { // class members } 

Abstract classes

As with Java, in C# you can use the abstract modifier in a class declaration to indicate that the class should not (and cannot) be instantiated. Classes derived from abstract classes must implement all the abstract methods of the class, and the sealed modifier cannot be applied to these methods.

Preventing inheritance

In C# the sealed modifier is used to prevent accidental inheritance, because a class defined as sealed can not be inherited from. Declaring a class as final achieves the same goal. Declaring a method as final also seals it, making it impossible to override. Declaring a variable as final is essentially making it read- only; however, you can still set a final value to the value of a variable. (This is different from constants, where the value of constants must be known at compile time so constants may only be set equal to other constants.)

Using base class members and base constructors

The keyword this works the same in Java and C#. In Java the super reference variable is used to signify the immediate parent class. In C# the equivalent is base. Take a C# class CalculateFor that provides the ability to work out the value of integer x raised to a particular integer power (for example, x raised to the power of three is x multiplied by x multiplied by x), given x and the power (provided an overflow does not occur):

 using System; public class CalculateFor { internal int x; public CalculateFor(int x) { this.x = x; } public int ToThePower(int power) { int total = 1; for(int i = 0; i < power; i ++) { total *= x; } return total; } } 

You could use this class in other code like this, given a value of x of 9 and a value of power of 3:

CalculateFor myNumber = new CalculateFor(9);  int result = myNumber.ToThePower(3);

The following code introduces a subclass of CalculateFor, ExpCalculateFor, which contains a member floating-point variable, and the method ToTheExponent() that multiplies the result of ten to a particular power by that floating point value:

 using System; public class ExpCalculateFor { internal float y; public ExpCalculateFor(float y) : CalculateFor(10) { this.y = y; } public int ToTheExponent(int power) { int total = 1; for(int i = 0; i < power; i ++) { total *= base.x; } total *= y; return total; } } 

Notice the syntax used when referring to a base constructor in a subclass's constructor declaration. Actually, you could simplify the ToTheExponent() method to the following, reusing the functionality of the base class's ToThePower() method:

 public int ToTheExponent(int power) { float total = (base.x).(base.ToThePower(power)); total *= y; return total; } 

Method overriding and hiding

In C#, method overriding is a very explicit procedure. This is quite different from the Java approach, where overriding is the default behavior when the signature of a super class member is the same as the signature of its subclass. In C#, to provide method overriding functionality, the modifiers virtual and override are used in tandem. All methods in the base class that you expect will be overridden must use the virtual keyword. To override them, use the override keyword in the child class. The following code uses an example class and subclass to demonstrate the override functionality:

 using System; public class FruitPlant { public FruitPlant(){} public virtual void BearFruit() { Console.WriteLine("Generic fruit plant"); } } class MangoTree : FruitPlant { public MangoTree(){} public MangoTree(){} public override void BearFruit() { Console.WriteLine("Tree fruit is:->Mango"); } } public class FruitPlantTest { public FruitPlantTest(){} public static void Main(string[] args) { FruitPlant p = new FruitPlant(); p.BearFruit(); MangoTree t = new MangoTree(); t.BearFruit(); ((FruitPlant)t).BearFruit(); } } 

Compiling and running this code produces the following output:

Generic fruit plant Tree fruit is:->Mango Tree fruit is:->Mango 

As you can see, the most derived Fruit() method is called, irrespective of the use of final cast of the MangoTree instance to the Plant instance. Indeed, the benefit of using method overriding is that you are guaranteed that the most derived method will always be called.

Although you cannot override a method in C# unless the method was originally declared as virtual, C# also introduces a new concept, method hiding. This allows developers to redefine super-class members in the child class and hide the base class implementation even if the base member is not declared virtual. C# uses the new modifier to accomplish this.

The benefit of hiding members from the base class rather than overriding them is that you can selectively determine which implementation to use. By modifying the previous code, you can see this concept in action:

 public class FruitPlant { public FruitPlant(){} public void BearFruit() { Console.WriteLine("Generic fruit plant"); } } class MangoTree : FruitPlant { public MangoTree(){} new public void BearFruit() { Console.WriteLine("Tree fruit is:->Mango"); } } // then FruitPlantTest implementation 

Running this example produces this output:

Generic plant fruit Tree fruit is:->Mango Generic plant fruit

In other words, unlike overriding, when hiding methods, the method invoked depends on the object the method is called on. For the last line of output, you cast the MangoTree instance back to a Plant instance before calling the BearFruit() method. So the Plant class's method is called.

You should note that the new modifier can also be used to hide any other type of inherited members from base class members of a similar signature.




Professional C# 2005
Pro Visual C++ 2005 for C# Developers
ISBN: 1590596080
EAN: 2147483647
Year: 2005
Pages: 351
Authors: Dean C. Wills

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net