Most modern programming languages include some form of runtime that provides common services and access to the underlying operating systems and hardware. Examples of this range from a simple functional library, such as the ANSI C Runtime used by C and C++, to the rich object-oriented class libraries provided by the Java Runtime Environment.
Similar to the way that Java programs depend on the Java class libraries and virtual machine, C# programs depend on the services in the .NET Framework such as the base class libraries (BCL) and the Common Language Runtime (CLR).
For a high-level overview of the BCL, see Chapter 4.
This chapter addresses the most common tasks you need to perform when building C# programs. These topics generally fall into one of two categories: leveraging functionality included in the BCL and interacting with elements of the CLR.
Certain types in the BCL are ubiquitous, in that they are fundamental to the way the BCL and CLR work and provide common functionality used throughout the entire BCL.
This section identifies some of the most common of these types and
provides guidelines on their usage. The types mentioned in this
section all exist in the
System
namespace.
The
System.Object
class is the root of the class
hierarchy and serves as the base class for every other class. The
C#
object
type aliases
System.Object
. System.Object
provides a handful of useful methods that are present on all objects,
and whose signatures are listed in the following fragment of
the
System.Object
class definition:
public class Object { public Object( ) {...} public virtual bool Equals(object o) {...} public virtual int GetHashCode( ){...} public Type GetType( ){...} public virtual string ToString( ) {...} protected virtual void Finalize( ) {...} protected object MemberwiseClone( ) {...} }
Object( )
The constructor for the Object
base class.
Equals(object o)
This method evaluates whether two objects are equivalent.
The default implementation of this method compares the objects by reference, so classes are expected to override this method to compare two objects by value.
In C#, you can also override the
==
and !=
operators. For
more information see Section 2.9.8.1 in
Section 2.9 in Chapter 2.
GetHashCode( )
This method allows objects to provide their own hash function for use in collections.
The return value from this function should pass the following tests: (1) two objects representing the same value should return the same hashcode, and (2) the returned values should generate a random distribution at runtime.
The default implementation of
GetHashCode
actually
doesn’t meet these criteria, as it merely returns a number
based on the object reference. For this reason, you should usually
override this method in your own types.
To learn more about how the hashcode is used by the predefined collection classes, see Section 3.4, later in this chapter.
GetType( )
This method provides access to the
Type
object representing the type of the
object, and should never be implemented by your types. To learn more
about the Type
object and reflection
in general, see Section 3.10.
ToString( )
This method provides a string representation of the object and is generally intended for use when debugging.
The default implementation of this method merely returns the name of
the type and should be overridden in your own types to return a
meaningful string representation of the object. The predefined types
such as int
and string
, all
override this method to return the value, as follows:
using System; class Beeblebrox {} class Test { static void Main( ) { string s = "Zaphod"; Beeblebrox b = new Beeblebrox( ); Console.WriteLine(s); // Prints "Zaphod" Console.WriteLine(b); // Prints "Beeblebrox" } }
Finalize( )
The
Finalize
method
cleans up nonmemory resources and is usually called by the garbage
collector before reclaiming the memory for the object. The
Finalize
method can be overridden on any reference
type, but this should be done only in a very few cases. For a
discussion of finalizers and the garbage collector, see Section 3.12.
MemberwiseClone( )
This method creates shallow copies of the object and should never be implemented by your types. To learn how to control shallow/deep copy semantics on your own types, see Section 3.1.2.
When defining new types that work well with the rest of the BCL, you should override several of these methods as appropriate. Some of these overrides have parallels with C# operators that can also be overridden where it makes sense.
Here is an example of a new value type that is intended to be a good citizen in the BCL:
public class Point3D { public int x, y, z; public Point3D(int x, int y, int z) { this.x=x; this.y=y; this.z=z; // Initialize data } public override bool Equals(object o) { if (!(o is Point3D)) // Check for type equivalence return false; return (this==(Point3D)o); // Implemented by operator== } public static bool operator !=(Point3D lhs, Point3D rhs) { return (!(lhs==rhs)); // Implemented by operator== } public static bool operator ==(Point3D lhs, Point3D rhs) { return ((rhs.x==lhs.x) && (rhs.y==lhs.y) && (rhs.z==lhs.z)); } public override int GetHashCode( ){ return x^y^z; } public override string ToString( ) { return String.Format("[{0},{1},{2}]", x, y, z); } }
This class overrides
Equals
,
operator==
, and operator!=
to
provide value-based equality semantics, creates a hashcode that
follows the rules described in the preceding section, and overrides
ToString
for easy debugging. It can be used as
follows:
using System; using System.Collections; public class Point3D {...} class TestPoint3D { static void Main( ) { // Uses ToString, prints "p1=[1,1,1] p2=[2,2,2] p3=[2,2,2]" Point3D p1 = new Point3D(1,1,1); Point3D p2 = new Point3D(2,2,2); Point3D p3 = new Point3D(2,2,2); Console.WriteLine("p1={0} p2={1} p3={2}", p1, p2, p3); // Tests for equality to demonstrate Equals, == & != int i = 100; Console.WriteLine(p1.Equals(i)); // Prints "False" Console.WriteLine(p1==p2); // Prints "False" Console.WriteLine(p2==p3); // Prints "True" // Use a hashtable to store points (uses GetHashCode) Hashtable ht = new Hashtable( ); ht["p1"] = p1; ht["p2"] = p2; ht["p3"] = p3; // Prints "p2=[2,2,2] p3=[2,2,2] p1=[1,1,1]" foreach (DictionaryEntry de in ht) Console.Write("{0}={1} ", de.Key, de.Value); } }
public interface ICloneable { object Clone( ); }
ICloneable
allows classes or structs instances to be
cloned. It contains a single method named Clone that returns a copy
of the instance. When implementing this interface your Clone method
can either simply return this.MemberwiseClone( )
,
which performs a shallow copy (the fields are copied directly), or
you can perform a custom deep copy, where you clone individual fields
in the class or struct. The
following example is the simplest implementation
ICloneable
:
public class Foo : ICloneable { public object Clone( ) { return this.MemberwiseClone( ); } }
interface IComparable { int CompareTo(object o); }
IComparable
is implemented by types that have instances that can be ordered (see
Section 3.4). It contains a
single method named CompareTo
that:
Returns - if instance < o
Returns + if instance > o
Returns 0 if instance == o
This interface is implemented by all numeric types,
string
, DateTime
, etc. It may
also be implemented by custom classes or structs to provide
comparison semantics. For example:
using System; using System.Collections; class MyType : IComparable { public int x; public MyType(int x) { this.x = x; } public int CompareTo(object o) { return x -((MyType)o).x; } } class Test { static void Main( ) { ArrayList a = new ArrayList( ); a.Add(new MyType(42)); a.Add(new MyType(17)); a.Sort( ); foreach(MyType t in a) Console.WriteLine(((MyType)t).x); } }
public interface IFormattable { string Format(string format, IServiceObjectProvider sop); }
The IFormattable
interface is implemented by types that have formatting options for
converting their value to a string representation. For instance, a
decimal
may be converted to a
string
representing currency, or a
string
which uses a comma for a decimal point. The
formatting options are specified by the format
string (see Section 3.3.4). If an
IServiceObjectProvider
interface is supplied, it specifies the specific culture
to be used for the conversion.
IFormattable
is commonly used when calling one of
the String
class Format
methods
(see Section 3.3).
All the common types (int
,
string
, DateTime
, etc.)
implement this interface, and you should implement it on your own
types if you want them to be fully supported by the
String
class when formatting.
18.117.230.81