C 12 Pocket Reference Instant Help For C 12 Programmers Converted Joseph Albahari
C 12 Pocket Reference Instant Help For C 12 Programmers Converted Joseph Albahari
C 12 Pocket Reference Instant Help For C 12 Programmers Converted Joseph Albahari
C 12 Pocket Reference Instant Help For C 12 Programmers Converted Joseph Albahari
1. C 12 Pocket Reference Instant Help For C 12
Programmers Converted Joseph Albahari download
https://ebookbell.com/product/c-12-pocket-reference-instant-help-
for-c-12-programmers-converted-joseph-albahari-53329644
Explore and download more ebooks at ebookbell.com
2. Here are some recommended products that we believe you will be
interested in. You can click the link to download.
C 10 Pocket Reference Instant Help For C 10 Programmers First Early
Release 1 20211207 First Early Release Joseph Albahari
https://ebookbell.com/product/c-10-pocket-reference-instant-help-
for-c-10-programmers-first-early-release-1-20211207-first-early-
release-joseph-albahari-37364154
C 12 Pocket Reference Joseph Albahari Ben Albahari
https://ebookbell.com/product/c-12-pocket-reference-joseph-albahari-
ben-albahari-56725098
C 10 Pocket Reference 20211207 First Early Release Joseph Albahari Ben
Albahari
https://ebookbell.com/product/c-10-pocket-reference-20211207-first-
early-release-joseph-albahari-ben-albahari-36533576
Joe Picket 712 C J Box
https://ebookbell.com/product/joe-picket-712-c-j-box-55341598
3. C 12 In A Nutshell Joseph Albahari
https://ebookbell.com/product/c-12-in-a-nutshell-joseph-
albahari-53522124
C 12 And Net 8 Modern Crossplatform Development Fundamentals Start
Building Websites And Services With Aspnet Core 8 Blazor And Ef Core 8
8th Edition Eighth Mark J Price
https://ebookbell.com/product/c-12-and-net-8-modern-crossplatform-
development-fundamentals-start-building-websites-and-services-with-
aspnet-core-8-blazor-and-ef-core-8-8th-edition-eighth-mark-j-
price-54870546
C 12 For Cloud Web And Desktop Applications Modern Concepts And
Techniques For Software Development With C 12 1st Edition Thiago Vivas
De Araujo
https://ebookbell.com/product/c-12-for-cloud-web-and-desktop-
applications-modern-concepts-and-techniques-for-software-development-
with-c-12-1st-edition-thiago-vivas-de-araujo-145860460
C 12 Www The Client Side Software Development Poul Klausen
https://ebookbell.com/product/c-12-www-the-client-side-software-
development-poul-klausen-47532160
C 12 And Net 8 Modern Crossplatform Development Fundamentals 8th
Edition Mark J Price
https://ebookbell.com/product/c-12-and-net-8-modern-crossplatform-
development-fundamentals-8th-edition-mark-j-price-52769386
6. C# 12 Pocket Reference
Instant Help for C# 12 Programmers
Joseph Albahari and Ben Albahari
8. 2023-10-27: First Release
See https://oreil.ly/c12prERR for release details.
The O’Reilly logo is a registered trademark of O’Reilly Media, Inc. C# 12
Pocket Reference, the cover image, and related trade dress are trademarks of
O’Reilly Media, Inc.
The views expressed in this work are those of the authors and do not
represent the publisher’s views. While the publisher and the authors have
used good faith efforts to ensure that the information and instructions
contained in this work are accurate, the publisher and the authors disclaim all
responsibility for errors or omissions, including without limitation
responsibility for damages resulting from the use of or reliance on this work.
Use of the information and instructions contained in this work is at your own
risk. If any code samples or other technology this work contains or describes
is subject to open source licenses or the intellectual property rights of others,
it is your responsibility to ensure that your use thereof complies with such
licenses and/or rights.
978-1-098-14754-9
[LSI]
9. C# 12 Pocket Reference
C# is a general-purpose, type-safe, primarily object-oriented programming
language, the goal of which is programmer productivity. To this end, the
language balances simplicity, expressiveness, and performance. C# 12 is
designed to work with the Microsoft .NET 8 runtime (whereas C# 11 targets
.NET 7, C# 10 targets .NET 6, and C# 7 targets Microsoft .NET Framework
4.6/4.7/4.8).
NOTE
The programs and code snippets in this book mirror those in Chapters 2 through 4 of C# 12 in a
Nutshell and are all available as interactive samples in LINQPad. Working through these samples in
conjunction with the book accelerates learning in that you can edit the samples and instantly see the
results without needing to set up projects and solutions in Visual Studio.
To download the samples, click the Samples tab in LINQPad and then click “Download more
samples.” LINQPad is free—go to www.linqpad.net.
A First C# Program
Following is a program that multiplies 12 by 30 and prints the result, 360, to
the screen. The double forward slash indicates that the remainder of a line is a
comment:
int x = 12 * 30; // Statement 1
System.Console.WriteLine (x); // Statement 2
Our program consists of two statements. Statements in C# execute
sequentially and are terminated by a semicolon. The first statement computes
the expression 12 * 30 and stores the result in a variable, named x, whose
type is a 32-bit integer (int). The second statement calls the WriteLine
method on a class called Console, which is defined in a namespace called
10. System. This prints the variable x to a text window on the screen.
A method performs a function; a class groups function members and data
members to form an object-oriented building block. The Console class
groups members that handle command-line input/output (I/O) functionality,
such as the WriteLine method. A class is a kind of type, which we
examine in “Type Basics”.
At the outermost level, types are organized into namespaces. Many
commonly used types—including the Console class—reside in the
System namespace. The .NET libraries are organized into nested
namespaces. For example, the System.Text namespace contains types for
handling text, and System.IO contains types for input/output.
Qualifying the Console class with the System namespace on every use
adds clutter. The using directive lets you avoid this clutter by importing a
namespace:
using System; // Import the System namespace
int x = 12 * 30;
Console.WriteLine (x); // No need to specify System
A basic form of code reuse is to write higher-level functions that call lower-
level functions. We can refactor our program with a reusable method called
FeetToInches that multiplies an integer by 12, as follows:
using System;
Console.WriteLine (FeetToInches (30)); // 360
Console.WriteLine (FeetToInches (100)); // 1200
int FeetToInches (int feet)
{
int inches = feet * 12;
return inches;
}
Our method contains a series of statements surrounded by a pair of braces.
11. This is called a statement block.
A method can receive input data from the caller by specifying parameters
and output data back to the caller by specifying a return type. Our
FeetToInches method has a parameter for inputting feet, and a return
type for outputting inches:
int FeetToInches (int feet)
...
The literals 30 and 100 are the arguments passed to the FeetToInches
method.
If a method doesn’t receive input, use empty parentheses. If it doesn’t return
anything, use the void keyword:
using System;
SayHello();
void SayHello()
{
Console.WriteLine ("Hello, world");
}
Methods are one of several kinds of functions in C#. Another kind of
function we used in our example program was the * operator, which
performs multiplication. There are also constructors, properties, events,
indexers, and finalizers.
Compilation
The C# compiler compiles source code (a set of files with the .cs extension)
into an assembly. An assembly is the unit of packaging and deployment in
.NET. An assembly can be either an application or a library. A normal
console or Windows application has an entry point, whereas a library does
not. The purpose of a library is to be called upon (referenced) by an
application or by other libraries. .NET itself is a set of libraries (as well as a
runtime environment).
12. Each of the programs in the preceding section began directly with a series of
statements (called top-level statements). The presence of top-level statements
implicitly creates an entry point for a console or Windows application.
(Without top-level statements, a Main method denotes an application’s entry
point—see “Symmetry of predefined types and custom types”.)
To invoke the compiler, you can either use an integrated development
environment (IDE) such as Visual Studio or Visual Studio Code, or call it
manually from the command line. To manually compile a console application
with .NET, first download the .NET 8 SDK, and then create a new project, as
follows:
dotnet new console -o MyFirstProgram
cd MyFirstProgram
This creates a folder called MyFirstProgram, which contains a C# file called
Program.cs, which you can then edit. To invoke the compiler, call dotnet
build (or dotnet run, which will compile and then run the program).
The output will be written to a subdirectory under bindebug, which will
include MyFirstProgram.dll (the output assembly) as well as
MyFirstProgram.exe (which runs the compiled program directly).
Syntax
C# syntax is inspired by C and C++ syntax. In this section, we describe C#’s
elements of syntax, using the following program:
using System;
int x = 12 * 30;
Console.WriteLine (x);
Identifiers and Keywords
Identifiers are names that programmers choose for their classes, methods,
variables, and so on. Here are the identifiers in our example program, in the
13. order in which they appear:
System x Console WriteLine
An identifier must be a whole word, essentially made up of Unicode
characters starting with a letter or underscore. C# identifiers are case
sensitive. By convention, parameters, local variables, and private fields
should be in camel case (e.g., myVariable), and all other identifiers should
be in Pascal case (e.g., MyMethod).
Keywords are names that mean something special to the compiler. There are
two keywords in our example program, using and int.
Most keywords are reserved, which means that you can’t use them as
identifiers. Here is the full list of C# reserved keywords:
abstract
as
base
bool
break
byte
case
catch
char
checked
class
const
continue
decimal
default
delegate
do
double
else
enum
event
explicit
extern
false
finally
fixed
float
for
foreach
goto
if
implicit
in
int
interface
internal
is
lock
long
namespace
new
null
object
operator
out
override
params
private
protected
public
readonly
record
ref
return
sbyte
sealed
short
sizeof
stackalloc
static
string
struct
switch
this
throw
true
try
typeof
uint
ulong
unchecked
unsafe
ushort
using
virtual
void
volatile
while
Avoiding conflicts
If you really want to use an identifier that clashes with a reserved keyword,
14. you can do so by qualifying it with the @ prefix. For instance:
class class {...} // Illegal
class @class {...} // Legal
The @ symbol doesn’t form part of the identifier itself. So @myVariable is
the same as myVariable.
Contextual keywords
Some keywords are contextual, meaning they can also be used as identifiers
—without an @ symbol. The contextual keywords are as follows:
add
alias
and
ascending
async
await
by
descending
dynamic
equals
file
from
get
global
group
init
into
join
let
managed
nameof
nint
not
notnull
nuint
on
or
orderby
partial
remove
required
select
set
unmanaged
value
var
with
when
where
yield
With contextual keywords, ambiguity cannot arise within the context in
which they are used.
Literals, Punctuators, and Operators
Literals are primitive pieces of data lexically embedded into the program.
The literals we used in our example program are 12 and 30. Punctuators
help demarcate the structure of the program. An example is the semicolon,
which terminates a statement. Statements can wrap multiple lines:
Console.WriteLine
(1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10);
An operator transforms and combines expressions. Most operators in C# are
15. denoted with a symbol, such as the multiplication operator, *. Here are the
operators in our program:
= * . ()
A period denotes a member of something (or a decimal point with numeric
literals). Parentheses are used when declaring or calling a method; empty
parentheses are used when the method accepts no arguments. The equals sign
performs assignment (the double equals sign, ==, performs equality
comparison).
Comments
C# offers two different styles of source code documentation: single-line
comments and multiline comments. A single-line comment begins with a
double forward slash and continues until the end of the line. For example:
int x = 3; // Comment about assigning 3 to x
A multiline comment begins with /* and ends with */. For example:
int x = 3; /* This is a comment that
spans two lines */
Comments can embed XML documentation tags (see “XML
Documentation”).
Type Basics
A type defines the blueprint for a value. In our example, we used two literals
of type int with values 12 and 30. We also declared a variable of type int
whose name was x.
A variable denotes a storage location that can contain different values over
time. In contrast, a constant always represents the same value (more on this
later).
16. All values in C# are an instance of a specific type. The meaning of a value,
and the set of possible values a variable can have, is determined by its type.
Predefined Type Examples
Predefined types (also called built-in types) are types that are specially
supported by the compiler. The int type is a predefined type for
representing the set of integers that fit into 32 bits of memory, from −231 to
231−1. We can perform functions such as arithmetic with instances of the
int type as follows:
int x = 12 * 30;
Another predefined C# type is string. The string type represents a
sequence of characters, such as “.NET” or “http://oreilly.com. We can work
with strings by calling functions on them, as follows:
string message = "Hello world";
string upperMessage = message.ToUpper();
Console.WriteLine (upperMessage); // HELLO WORLD
int x = 2022;
message = message + x.ToString();
Console.WriteLine (message); // Hello world2022
The predefined bool type has exactly two possible values: true and
false. The bool type is commonly used to conditionally branch execution
flow with an if statement. For example:
bool simpleVar = false;
if (simpleVar)
Console.WriteLine ("This will not print");
int x = 5000;
bool lessThanAMile = x < 5280;
if (lessThanAMile)
Console.WriteLine ("This will print");
17. The System namespace in .NET contains many important types that are not
predefined by C# (e.g., DateTime).
Custom Type Examples
Just as you can build complex functions from simple functions, you can build
complex types from primitive types. In this example, we will define a custom
type named UnitConverter—a class that serves as a blueprint for unit
conversions:
UnitConverter feetToInches = new UnitConverter (12);
UnitConverter milesToFeet = new UnitConverter (5280);
Console.WriteLine (feetToInches.Convert(30)); // 360
Console.WriteLine (feetToInches.Convert(100)); // 1200
Console.WriteLine (feetToInches.Convert
(milesToFeet.Convert(1))); // 63360
public class UnitConverter
{
int ratio; // Field
public UnitConverter (int unitRatio) // Constructor
{
ratio = unitRatio;
}
public int Convert (int unit) // Method
{
return unit * ratio;
}
}
Members of a type
A type contains data members and function members. The data member of
UnitConverter is the field called ratio. The function members of
UnitConverter are the Convert method and the UnitConverter’s
constructor.
Symmetry of predefined types and custom types
18. A beautiful aspect of C# is that predefined types and custom types have few
differences. The predefined int type serves as a blueprint for integers. It
holds data—32 bits—and provides function members that use that data, such
as ToString. Similarly, our custom UnitConverter type acts as a
blueprint for unit conversions. It holds data—the ratio—and provides
function members to use that data.
Constructors and instantiation
Data is created by instantiating a type. You can instantiate predefined types
simply by using a literal such as 12 or "Hello world".
The new operator creates instances of a custom type. We started our program
by creating two instances of the UnitConverter type. Immediately after
the new operator instantiates an object, the object’s constructor is called to
perform initialization. A constructor is defined like a method, except that the
method name and return type are reduced to the name of the enclosing type:
public UnitConverter (int unitRatio) // Constructor
{
ratio = unitRatio;
}
Instance versus static members
The data members and function members that operate on the instance of the
type are called instance members. UnitConverter’s Convert method
and int’s ToString method are examples of instance members. By
default, members are instance members.
Data members and function members that don’t operate on the instance of the
type can be marked as static. To refer to a static member from outside its
type, you specify its type name rather than an instance. An example is the
WriteLine method of the Console class. Because this is static, we call
Console.WriteLine() and not new Console().WriteLine().
In the following code, the instance field Name pertains to an instance of a
particular Panda, whereas Population pertains to the set of all Panda
19. instances. We create two instances of the Panda, print their names, and then
print the total population:
Panda p1 = new Panda ("Pan Dee");
Panda p2 = new Panda ("Pan Dah");
Console.WriteLine (p1.Name); // Pan Dee
Console.WriteLine (p2.Name); // Pan Dah
Console.WriteLine (Panda.Population); // 2
public class Panda
{
public string Name; // Instance field
public static int Population; // Static field
public Panda (string n) // Constructor
{
Name = n; // Instance field
Population = Population + 1; // Static field
}
}
Attempting to evaluate p1.Population or Panda.Name will generate a
compile-time error.
The public keyword
The public keyword exposes members to other classes. In this example, if
the Name field in Panda was not marked as public, it would be private and
could not be accessed from outside the class. Marking a member public is
how a type communicates: “Here is what I want other types to see—
everything else is my own private implementation details.” In object-oriented
terms, we say that the public members encapsulate the private members of
the class.
Creating a namespace
Particularly with larger programs, it makes sense to organize types into
namespaces. Here’s how to define the Panda class inside a namespace
called Animals:
20. namespace Animals
{
public class Panda
{
...
}
}
We cover namespaces in detail in “Namespaces”.
Defining a Main method
All of our examples so far have used top-level statements, a feature that was
introduced in C# 9. Without top-level statements, a simple console or
Windows application looks like this:
using System;
class Program
{
static void Main() // Program entry point
{
int x = 12 * 30;
Console.WriteLine (x);
}
}
In the absence of top-level statements, C# looks for a static method called
Main, which becomes the entry point. The Main method can be defined
inside any class (and only one Main method can exist).
The Main method can optionally return an integer (rather than void) in
order to return a value to the execution environment (where a nonzero value
typically indicates an error). The Main method can also optionally accept an
array of strings as a parameter (that will be populated with any arguments
passed to the executable); for example:
static int Main (string[] args) {...}
NOTE
21. An array (such as string[]) represents a fixed number of elements of a particular type. Arrays are
specified by placing square brackets after the element type. We describe them in “Arrays”.
(The Main method can also be declared async and return a Task or
Task<int> in support of asynchronous programming—see “Asynchronous
Functions”.)
Top-level statements
Top-level statements let you avoid the baggage of a static Main method and
a containing class. A file with top-level statements comprises three parts, in
this order:
1. (Optionally) using directives
2. A series of statements, optionally mixed with method declarations
3. (Optionally) Type and namespace declarations
Everything in Part 2 ends up inside a compiler-generated “main” method,
inside a compiler-generated class. This means that the methods in your top-
level statements become local methods (we describe the subtleties in “Local
methods”). Top-level statements can optionally return an integer value to the
caller, and access a “magic” variable of type string[] called args,
corresponding to command-line arguments passed by the caller.
As a program can have only one entry point, there can be at most one file
with top-level statements in a C# project.
Types and Conversions
C# can convert between instances of compatible types. A conversion always
creates a new value from an existing one. Conversions can be either implicit
or explicit: implicit conversions happen automatically, whereas explicit
conversions require a cast. In the following example, we implicitly convert an
int to a long type (which has twice the bit capacity of an int) and
explicitly cast an int to a short type (which has half the bit capacity of an
22. int):
int x = 12345; // int is a 32-bit integer
long y = x; // Implicit conversion to 64-bit int
short z = (short)x; // Explicit conversion to 16-bit int
In general, implicit conversions are allowed when the compiler can guarantee
that they will always succeed without loss of information. Otherwise, you
must perform an explicit cast to convert between compatible types.
Value Types Versus Reference Types
C# types can be divided into value types and reference types.
Value types comprise most built-in types (specifically, all numeric types, the
char type, and the bool type) as well as custom struct and enum types.
Reference types comprise all class, array, delegate, and interface types.
The fundamental difference between value types and reference types is how
they are handled in memory.
Value types
The content of a value type variable or constant is simply a value. For
example, the content of the built-in value type int is 32 bits of data.
You can define a custom value type with the struct keyword (see
Figure 1):
public struct Point { public int X, Y; }
Figure 1. A value type instance in memory
23. The assignment of a value type instance always copies the instance. For
example:
Point p1 = new Point();
p1.X = 7;
Point p2 = p1; // Assignment causes copy
Console.WriteLine (p1.X); // 7
Console.WriteLine (p2.X); // 7
p1.X = 9; // Change p1.X
Console.WriteLine (p1.X); // 9
Console.WriteLine (p2.X); // 7
Figure 2 shows that p1 and p2 have independent storage.
Figure 2. Assignment copies a value type instance
Reference types
A reference type is more complex than a value type, having two parts: an
object and the reference to that object. The content of a reference type
variable or constant is a reference to an object that contains the value. Here is
the Point type from our previous example rewritten as a class (see
Figure 3):
public class Point { public int X, Y; }
24. Figure 3. A reference type instance in memory
Assigning a reference type variable copies the reference, not the object
instance. This allows multiple variables to refer to the same object—
something that’s not ordinarily possible with value types. If we repeat the
previous example, but with Point now a class, an operation via p1 affects
p2:
Point p1 = new Point();
p1.X = 7;
Point p2 = p1; // Copies p1 reference
Console.WriteLine (p1.X); // 7
Console.WriteLine (p2.X); // 7
p1.X = 9; // Change p1.X
Console.WriteLine (p1.X); // 9
Console.WriteLine (p2.X); // 9
Figure 4 shows that p1 and p2 are two references that point to the same
object.
25. Figure 4. Assignment copies a reference
Null
A reference can be assigned the literal null, indicating that the reference
points to no object. Assuming Point is a class:
Point p = null;
Console.WriteLine (p == null); // True
Accessing a member of a null reference generates a runtime error:
Console.WriteLine (p.X); // NullReferenceException
NOTE
In “Nullable Reference Types”, we describe a feature of C# that reduces accidental
NullReferenceExcep tion errors.
In contrast, a value type cannot ordinarily have a null value:
struct Point {...}
...
Point p = null; // Compile-time error
int x = null; // Compile-time error
26. To work around this, C# has a special construct for representing value-type
nulls—see “Nullable Value Types”.
Predefined Type Taxonomy
The predefined types in C# are as follows:
Value types
Numeric:
Signed integer (sbyte, short, int, long)
Unsigned integer (byte, ushort, uint, ulong)
Real number (float, double, decimal)
Logical (bool)
Character (char)
Reference types
String (string)
Object (object)
Predefined types in C# alias .NET types in the System namespace. There is
only a syntactic difference between these two statements:
int i = 5;
System.Int32 i = 5;
The set of predefined value types excluding decimal are known as
primitive types in the Common Language Runtime (CLR). Primitive types are
so called because they are supported directly via instructions in compiled
code, which usually translates to direct support on the underlying processor.
27. Numeric Types
C# has the following predefined numeric types:
C# type System type Suffix Size Range
Integral—
signed
sbyte SByte 8 bits –27 to 2
short Int16 16 bits –215 to 2
int Int32 32 bits –231 to 2
long Int64 L 64 bits –263 to 2
nint IntPtr 32/64 bits
Integral—
unsigned
byte Byte 8 bits 0 to 28–1
ushort UInt16 16 bits 0 to 216
uint UInt32 U 32 bits 0 to 232
ulong UInt64 UL 64 bits 0 to 264
nuint UIntPtr 32/64 bits
Real
float Single F 32 bits ± (~10–45
1038)
28. double Double D 64 bits ± (~10–324
10308)
decimal Decimal M 128 bits ± (~10–28
1028)
Of the integral types, int and long are first-class citizens and are favored
by both C# and the runtime. The other integral types are typically used for
interoperability or when space efficiency is paramount. The nint and
nuint native-sized integer types are sized to match the address space of the
process at runtime (32 or 64 bits). These types can be useful when working
with pointers—we describe their nuances in Chapter 4 of C# 12 in a Nutshell
(O’Reilly).
Of the real number types, float and double are called floating-point
types and are typically used for scientific and graphical calculations. The
decimal type is typically used for financial calculations where base-10-
accurate arithmetic and high precision are required. (Technically, decimal
is a floating-point type, too, although it’s not generally referred to as such.)
Numeric Literals
Integral-typed literals can use decimal, hexadecimal, or binary notation;
hexadecimal is denoted with the 0x prefix (e.g., 0x7f is equivalent to 127),
and binary is denoted with the 0b prefix. Real literals can use decimal or
exponential notation such as 1E06. Underscores may be inserted within (or
before) a numeric literal to improve readability (e.g., 1_000_000).
Numeric literal type inference
By default, the compiler infers a numeric literal to be either double or an
integral type:
If the literal contains a decimal point or the exponential symbol (E), it is
a double.
29. Otherwise, the literal’s type is the first type in this list that can fit the
literal’s value: int, uint, long, and ulong.
For example:
Console.Write ( 1.0.GetType()); // Double (double)
Console.Write ( 1E06.GetType()); // Double (double)
Console.Write ( 1.GetType()); // Int32 (int)
Console.Write (0xF0000000.GetType()); // UInt32 (uint)
Console.Write (0x100000000.GetType()); // Int64 (long)
Numeric suffixes
The numeric suffixes listed in the preceding table explicitly define the type of
a literal:
decimal d = 3.5M; // M = decimal (case-insensitive)
The suffixes U and L are rarely necessary because the uint, long, and
ulong types can nearly always be either inferred or implicitly converted
from int:
long i = 5; // Implicit conversion from int to long
The D suffix is technically redundant in that all literals with a decimal point
are inferred to be double (and you can always add a decimal point to a
numeric literal). The F and M suffixes are the most useful and are mandatory
when you’re specifying fractional float or decimal literals. Without
suffixes, the following would not compile because 4.5 would be inferred to
be of type double, which has no implicit conversion to float or
decimal:
float f = 4.5F; // Won't compile without suffix
decimal d = -1.23M; // Won't compile without suffix
Numeric Conversions
30. Integral-to-integral conversions
Integral conversions are implicit when the destination type can represent
every possible value of the source type. Otherwise, an explicit conversion is
required. For example:
int x = 12345; // int is a 32-bit integral type
long y = x; // Implicit conversion to 64-bit int
short z = (short)x; // Explicit conversion to 16-bit int
Real-to-real conversions
A float can be implicitly converted to a double because a double can
represent every possible float value. The reverse conversion must be
explicit.
Conversions between decimal and other real types must be explicit.
Real-to-integral conversions
Conversions from integral types to real types are implicit, whereas the reverse
must be explicit. Converting from a floating-point to an integral type
truncates any fractional portion; to perform rounding conversions, use the
static System.Convert class.
A caveat is that implicitly converting a large integral type to a floating-point
type preserves magnitude but might occasionally lose precision:
int i1 = 100000001;
float f = i1; // Magnitude preserved, precision lost
int i2 = (int)f; // 100000000
Arithmetic Operators
The arithmetic operators (+, -, *, /, %) are defined for all numeric types
except the 8- and 16-bit integral types. The % operator evaluates the
remainder after division.
Increment and Decrement Operators
31. The increment and decrement operators (++, --, respectively) increment and
decrement numeric types by 1. The operator can either precede or follow the
variable, depending on whether you want the variable to be updated before or
after the expression is evaluated. For example:
int x = 0;
Console.WriteLine (x++); // Outputs 0; x is now 1
Console.WriteLine (++x); // Outputs 2; x is now 2
Console.WriteLine (--x); // Outputs 1; x is now 1
Specialized Integral Operations
Division
Division operations on integral types always eliminate the remainder (round
toward zero). Dividing by a variable whose value is zero generates a runtime
error (a DivideByZeroException). Dividing by the literal or constant 0
generates a compile-time error.
Overflow
At runtime, arithmetic operations on integral types can overflow. By default,
this happens silently—no exception is thrown, and the result exhibits
wraparound behavior, as though the computation were done on a larger
integer type and the extra significant bits discarded. For example,
decrementing the minimum possible int value results in the maximum
possible int value:
int a = int.MinValue; a--;
Console.WriteLine (a == int.MaxValue); // True
The checked and unchecked operators
The checked operator instructs the runtime to generate an
OverflowException rather than overflowing silently when an integral-
typed expression or statement exceeds the arithmetic limits of that type. The
checked operator affects expressions with the ++, −−, (unary) −, +, −, *, /,
and explicit conversion operators between integral types. Overflow checking
32. incurs a small performance cost.
You can use checked around either an expression or a statement block. For
example:
int a = 1000000, b = 1000000;
int c = checked (a * b); // Checks just the expression
checked // Checks all expressions
{ // in statement block
c = a * b;
...
}
You can make arithmetic overflow checking the default for all expressions in
a program by compiling with the /checked+ command-line switch (in
Visual Studio, go to Advanced Build Settings). If you then need to disable
overflow checking just for specific expressions or statements, you can do so
with the unchecked operator.
Bitwise operators
C# supports the following bitwise operators:
Operator Meaning Sample expression Result
~ Complement ~0xfU 0xfffffff0U
& And 0xf0 & 0x33 0x30
| Or 0xf0 | 0x33 0xf3
^ Exclusive Or 0xff00 ^ 0x0ff0 0xf0f0
<< Shift left 0x20 << 2 0x80
>> Shift right 0x20 >> 1 0x10
33. From C# 11, there is also an unsigned shift-right operator (>>>). Whereas the
shift-right operator (>>) replicates the high-order bit when operating on
signed integers, the unsigned shift-right operator (>>>) does not.
8- and 16-Bit Integral Types
The 8- and 16-bit integral types are byte, sbyte, short, and ushort.
These types lack their own arithmetic operators, so C# implicitly converts
them to larger types as required. This can cause a compilation error when
trying to assign the result back to a small integral type:
short x = 1, y = 1;
short z = x + y; // Compile-time error
In this case, x and y are implicitly converted to int so that the addition can
be performed. This means that the result is also an int, which cannot be
implicitly cast back to a short (because it could cause loss of data). To
make this compile, you must add an explicit cast:
short z = (short) (x + y); // OK
Special Float and Double Values
Unlike integral types, floating-point types have values that certain operations
treat specially. These special values are NaN (Not a Number), +∞, −∞, and
−0. The float and double classes have constants for NaN, +∞, and −∞
(as well as other values including MaxValue, MinValue, and Epsilon).
For example:
Console.Write (double.NegativeInfinity); // -Infinity
Dividing a nonzero number by zero results in an infinite value:
Console.WriteLine ( 1.0 / 0.0); // Infinity
Console.WriteLine (−1.0 / 0.0); // -Infinity
Console.WriteLine ( 1.0 / −0.0); // -Infinity
Console.WriteLine (−1.0 / −0.0); // Infinity
34. Dividing zero by zero, or subtracting infinity from infinity, results in a NaN:
Console.Write ( 0.0 / 0.0); // NaN
Console.Write ((1.0 / 0.0) − (1.0 / 0.0)); // NaN
When you use ==, a NaN value is never equal to another value, even another
NaN value. To test whether a value is NaN, you must use the
float.IsNaN or double.IsNaN method:
Console.WriteLine (0.0 / 0.0 == double.NaN); // False
Console.WriteLine (double.IsNaN (0.0 / 0.0)); // True
When you use object.Equals, however, two NaN values are equal:
bool isTrue = object.Equals (0.0/0.0, double.NaN);
double Versus decimal
double is useful for scientific computations (such as computing spatial
coordinates). decimal is useful for financial computations and values that
are manufactured rather than the result of real-world measurements. Here’s a
summary of the differences:
Feature double decimal
Internal
representation
Base 2 Base 10
Precision 15–16
significant
figures
28–29 significant figures
Range ±(~10−324 to
~10308)
±(~10−28 to ~1028)
Special values +0, −0, +∞, −∞, None
35. and NaN
Speed Native to
processor
Non-native to processor (about 10
times slower than double)
Real Number Rounding Errors
float and double internally represent numbers in base 2. For this reason,
most literals with a fractional component (which are in base 10) will not be
represented precisely, making them bad for financial calculations. In contrast,
decimal works in base 10 and so can precisely represent fractional
numbers such as 0.1 (whose base-10 representation is nonrecurring).
Boolean Type and Operators
C#’s bool type (aliasing the System.Boolean type) is a logical value
that can be assigned the literal true or false.
Although a Boolean value requires only one bit of storage, the runtime will
use one byte of memory because this is the minimum chunk that the runtime
and processor can efficiently work with. To avoid space inefficiency in the
case of arrays, .NET provides a BitArray class in the System
.Col lections namespace that is designed to use just one bit per
Boolean value.
Equality and Comparison Operators
== and != test for equality and inequality, respectively, of any type and
always return a bool value. Value types typically have a very simple notion
of equality:
int x = 1, y = 2, z = 1;
Console.WriteLine (x == y); // False
Console.WriteLine (x == z); // True
36. For reference types, equality, by default, is based on reference, as opposed to
the actual value of the underlying object. Therefore, two instances of an
object with identical data are not considered equal unless the == operator for
that type is specially overloaded to that effect (see “The object Type” and
“Operator Overloading”).
The equality and comparison operators, ==, !=, <, >, >=, and <=, work for
all numeric types but should be used with caution with real numbers (see
“Real Number Rounding Errors” in the previous section). The comparison
operators also work on enum type members by comparing their underlying
integral values.
Conditional Operators
The && and || operators test for and and or conditions, respectively. They
are frequently used in conjunction with the ! operator, which expresses not.
In the following example, the UseUmbrella method returns true if it’s
rainy or sunny (to protect us from the rain or the sun), as long as it’s not also
windy (because umbrellas are useless in the wind):
static bool UseUmbrella (bool rainy, bool sunny,
bool windy)
{
return !windy && (rainy || sunny);
}
The && and || operators short-circuit evaluation when possible. In the
preceding example, if it is windy, the expression (rainy || sunny) is
not even evaluated. Short-circuiting is essential in allowing expressions such
as the following to run without throwing a NullReferenceException:
if (sb != null && sb.Length > 0) ...
The & and | operators also test for and and or conditions:
return !windy & (rainy | sunny);
37. The difference is that they do not short-circuit. For this reason, they are
rarely used in place of conditional operators.
The ternary conditional operator (simply called the conditional operator) has
the form q ? a : b, where if condition q is true, a is evaluated, otherwise
b is evaluated. For example:
static int Max (int a, int b)
{
return (a > b) ? a : b;
}
The conditional operator is particularly useful in LINQ queries.
Strings and Characters
C#’s char type (aliasing the System.Char type) represents a Unicode
character and occupies two bytes (UTF-16). A char literal is specified
inside single quotes:
char c = 'A'; // Simple character
Escape sequences express characters that cannot be expressed or interpreted
literally. An escape sequence is a backslash followed by a character with a
special meaning. For example:
char newLine = 'n';
char backSlash = '';
The escape sequence characters are as follows:
Char Meaning Value
' Single quote 0x0027
" Double quote 0x0022
38. Backslash 0x005C
0 Null 0x0000
a Alert 0x0007
b Backspace 0x0008
f Form feed 0x000C
n New line 0x000A
r Carriage return 0x000D
t Horizontal tab 0x0009
v Vertical tab 0x000B
The u (or x) escape sequence lets you specify any Unicode character via
its four-digit hexadecimal code:
char copyrightSymbol = 'u00A9';
char omegaSymbol = 'u03A9';
char newLine = 'u000A';
An implicit conversion from a char to a numeric type works for the numeric
types that can accommodate an unsigned short. For other numeric types, an
explicit conversion is required.
String Type
C#’s string type (aliasing the System.String type) represents an
immutable (unmodifiable) sequence of Unicode characters. A string literal is
specified within double quotes:
string a = "Heat";
39. NOTE
string is a reference type rather than a value type. Its equality operators, however, follow value
type semantics:
string a = "test", b = "test";
Console.Write (a == b); // True
The escape sequences that are valid for char literals also work within
strings:
string a = "Here's a tab:t";
The cost of this is that whenever you need a literal backslash, you must write
it twice:
string a1 = "serverfilesharehelloworld.cs";
To avoid this problem, C# allows verbatim string literals. A verbatim string
literal is prefixed with @ and does not support escape sequences. The
following verbatim string is identical to the preceding one:
string a2 = @"serverfilesharehelloworld.cs";
A verbatim string literal can also span multiple lines. You can include the
double-quote character in a verbatim literal by writing it twice.
Raw string literals (C# 11)
Wrapping a string in three or more quote characters (""") creates a raw
string literal. Raw string literals can contain almost any character sequence,
without escaping or doubling up:
string raw = """<file path="c:temptest.txt"></file>""";
40. Raw string literals make it easy to represent JSON, XML, and HTML literals,
as well as regular expressions and source code. Should you need to include
three (or more) quote characters in the string itself, you can do so by
wrapping the string in four (or more) quote characters:
string raw = """"We can include """ in this string."""";
Multiline raw string literals are subject to special rules. We can represent the
string "Line 1rnLine 2" as follows:
string multiLineRaw = """
Line 1
Line 2
""";
Notice that the opening and closing quotes must be on separate lines to the
string content. Additionally:
Whitespace following the opening """ (on the same line) is ignored.
Whitespace preceding the closing """ (on the same line) is treated as
common indentation and is removed from every line in the string. This
lets you include indentation for source-code readability (as we did in our
example) without that indentation becoming part of the string.
Raw string literals can be interpolated, subject to special rules described in
“String interpolation”.
String concatenation
The + operator concatenates two strings:
string s = "a" + "b";
One of the operands can be a nonstring value, in which case ToString is
called on that value. For example:
string s = "a" + 5; // a5
41. Using the + operator repeatedly to build up a string can be inefficient; a better
solution is to use the System.Text.StringBuilder type—this
represents a mutable (editable) string and has methods to efficiently
Append, Insert, Remove, and Replace substrings.
String interpolation
A string preceded with the $ character is called an interpolated string.
Interpolated strings can include expressions within braces:
int x = 4;
Console.Write ($"A square has {x} sides");
// Prints: A square has 4 sides
Any valid C# expression of any type can appear within the braces, and C#
will convert the expression to a string by calling its ToString method or
equivalent. You can change the formatting by appending the expression with
a colon and a format string (we describe format strings in Chapter 6 of C# 12
in a Nutshell):
string s = $"15 in hex is {15:X2}";
// Evaluates to "15 in hex is 0F"
From C# 10, interpolated strings can be constants, as long as the interpolated
values are constants:
const string greeting = "Hello";
const string message = $"{greeting}, world";
From C# 11, interpolated strings are permitted to span multiple lines
(whether standard or verbatim):
string s = $"this interpolation spans {1 +
1} lines";
Raw string literals (from C# 11) can also be interpolated:
string s = $"""The date and time is {DateTime.Now}""";
42. To include a brace literal in an interpolated string:
With standard and verbatim string literals, repeat the desired brace
character.
With raw string literals, change the interpolation sequence by repeating
the $ prefix.
Using two (or more) $ characters in a raw string literal prefix changes the
interpolation sequence from one brace to two (or more) braces. Consider the
following string:
$$"""{ "TimeStamp": "{{DateTime.Now}}" }"""
This evaluates to:
{ "TimeStamp": "01/01/2024 12:13:25 PM" }
String comparisons
string does not support < and > operators for comparisons. You must
instead use string’s CompareTo method, which returns a positive
number, a negative number, or zero, depending on whether the first value
comes after, before, or alongside the second value:
Console.Write ("Boston".CompareTo ("Austin")); // 1
Console.Write ("Boston".CompareTo ("Boston")); // 0
Console.Write ("Boston".CompareTo ("Chicago")); // -1
Searching within strings
string’s indexer returns a character at a specified position:
Console.Write ("word"[2]); // r
The IndexOf and LastIndexOf methods search for a character within
the string. The Contains, StartsWith, and EndsWith methods search
43. for a substring within the string.
Manipulating strings
Because string is immutable, all the methods that “manipulate” a string
return a new one, leaving the original untouched:
Substring extracts a portion of a string.
Insert and Remove insert and remove characters at a specified
position.
PadLeft and PadRight add whitespace.
TrimStart, TrimEnd, and Trim remove whitespace.
The string class also defines ToUpper and ToLower methods for
changing case, a Split method to split a string into substrings (based on
supplied delimiters), and a static Join method to join substrings back into a
string.
UTF-8 Strings
From C# 11, you can use the u8 suffix to create string literals encoded in
UTF-8 rather than UTF-16. This feature is intended for advanced scenarios
such as the low-level handling of JSON text in performance hotspots:
ReadOnlySpan<byte> utf8 = "ab→cd"u8;
Console.WriteLine (utf8.Length); // 7
The underlying type is ReadOnlySpan<byte>, which we cover in
Chapter 23 of C# 12 in a Nutshell. You can convert this to an array by calling
the ToArray() method.
Arrays
An array represents a fixed number of elements of a particular type. The
44. elements in an array are always stored in a contiguous block of memory,
providing highly efficient access.
An array is denoted with square brackets after the element type. The
following declares an array of five characters:
char[] vowels = new char[5];
Square brackets also index the array, accessing a particular element by
position:
vowels[0] = 'a'; vowels[1] = 'e'; vowels[2] = 'i';
vowels[3] = 'o'; vowels[4] = 'u';
Console.WriteLine (vowels [1]); // e
This prints “e” because array indexes start at 0. You can use a for loop
statement to iterate through each element in the array. The for loop in this
example cycles the integer i from 0 to 4:
for (int i = 0; i < vowels.Length; i++)
Console.Write (vowels [i]); // aeiou
Arrays also implement IEnumerable<T> (see “Enumeration and
Iterators”), so you can also enumerate members with the foreach
statement:
foreach (char c in vowels) Console.Write (c); // aeiou
All array indexing is bounds-checked by the runtime. An
IndexOutOfRangeException is thrown if you use an invalid index:
vowels[5] = 'y'; // Runtime error
The Length property of an array returns the number of elements in the
array. After an array has been created, its length cannot be changed. The
System.Collection namespace and subnamespaces provide higher-
45. level data structures, such as dynamically sized arrays and dictionaries.
An array initialization expression lets you declare and populate an array in a
single step:
char[] vowels = new char[] {'a','e','i','o','u'};
Or simply:
char[] vowels = {'a','e','i','o','u'};
NOTE
From C# 12, you can use square brackets instead of curly braces:
char[] vowels = ['a','e','i','o','u'];
This is called a collection expression and has the advantage of also working when calling methods:
Foo (['a','e','i','o','u']);
void Foo (char[] letters) { ... }
Collection expressions also work with other collection types such as lists and sets—see “Collection
Initializers and Collection Expressions”.
All arrays inherit from the System.Array class, which defines common
methods and properties for all arrays. This includes instance properties such
as Length and Rank, and static methods to do the following:
Dynamically create an array (CreateInstance)
Get and set elements regardless of the array type
(GetValue/SetValue)
Search a sorted array (BinarySearch) or an unsorted array
(IndexOf, LastIndexOf, Find, FindIndex, FindLastIndex)
Sort an array (Sort)
46. Copy an array (Copy)
Default Element Initialization
Creating an array always preinitializes the elements with default values. The
default value for a type is the result of a bitwise zeroing of memory. For
example, consider creating an array of integers. Because int is a value type,
this allocates 1,000 integers in one contiguous block of memory. The default
value for each element will be 0:
int[] a = new int[1000];
Console.Write (a[123]); // 0
With reference type elements, the default value is null.
An array itself is always a reference type object, regardless of element type.
For instance, the following is legal:
int[] a = null;
Indices and Ranges
Indices and ranges (from C# 8) simplify working with elements or portions
of an array.
NOTE
Indices and ranges also work with the CLR types Span<T> and ReadOnlySpan<T>, which
provide efficient low-level access to managed or unmanaged memory.
You can also make your own types work with indices and ranges by defining an indexer of type
Index or Range (see “Indexers”).
Indices
Indices let you refer to elements relative to the end of an array, with the ^
operator. ^1 refers to the last element, ^2 refers to the second-to-last
47. element, and so on:
char[] vowels = new char[] {'a','e','i','o','u'};
char lastElement = vowels[^1]; // 'u'
char secondToLast = vowels[^2]; // 'o'
(^0 equals the length of the array, so vowels[^0] generates an error.)
C# implements indices with the help of the Index type, so you can also do
the following:
Index first = 0;
Index last = ^1;
char firstElement = vowels [first]; // 'a'
char lastElement = vowels [last]; // 'u'
Ranges
Ranges let you “slice” an array with the .. operator:
char[] firstTwo = vowels [..2]; // 'a', 'e'
char[] lastThree = vowels [2..]; // 'i', 'o', 'u'
char[] middleOne = vowels [2..3]; // 'i'
The second number in the range is exclusive, so ..2 returns the elements
before vowels[2].
You can also use the ^ symbol in ranges. The following returns the last two
characters:
char[] lastTwo = vowels [^2..^0]; // 'o', 'u'
(^0 is valid here because the second number in the range is exclusive.)
C# implements ranges with the help of the Range type, so you can also do
the following:
Range firstTwoRange = 0..2;
char[] firstTwo = vowels [firstTwoRange]; // 'a', 'e'
48. Multidimensional Arrays
Multidimensional arrays come in two varieties: rectangular and jagged.
Rectangular arrays represent an n-dimensional block of memory, and jagged
arrays are arrays of arrays.
Rectangular arrays
To declare rectangular arrays, use commas to separate each dimension. The
following declares a rectangular two-dimensional array, where the
dimensions are 3 × 3:
int[,] matrix = new int [3, 3];
The GetLength method of an array returns the length for a given
dimension (starting at 0):
for (int i = 0; i < matrix.GetLength(0); i++)
for (int j = 0; j < matrix.GetLength(1); j++)
matrix [i, j] = i * 3 + j;
A rectangular array can be initialized as follows (to create an array identical
to the previous example):
int[,] matrix = new int[,]
{
{0,1,2},
{3,4,5},
{6,7,8}
};
(The code shown in boldface can be omitted in declaration statements such as
this.)
Jagged arrays
To declare jagged arrays, use successive square-bracket pairs for each
dimension. Here is an example of declaring a jagged two-dimensional array,
for which the outermost dimension is 3:
49. int[][] matrix = new int[3][];
The inner dimensions aren’t specified in the declaration because, unlike a
rectangular array, each inner array can be an arbitrary length. Each inner
array is implicitly initialized to null rather than an empty array. Each inner
array must be created manually:
for (int i = 0; i < matrix.Length; i++)
{
matrix[i] = new int [3]; // Create inner array
for (int j = 0; j < matrix[i].Length; j++)
matrix[i][j] = i * 3 + j;
}
A jagged array can be initialized as follows (to create an array identical to the
previous example, but with an additional element at the end):
int[][] matrix = new int[][]
{
new int[] {0,1,2},
new int[] {3,4,5},
new int[] {6,7,8,9}
};
(The code shown in boldface can be omitted in declaration statements such as
this.)
Simplified Array Initialization Expressions
We’ve already seen how to simplify array initialization expressions by
omitting the new keyword and type declaration:
char[] vowels = new char[] {'a','e','i','o','u'};
char[] vowels = {'a','e','i','o','u'};
char[] vowels = ['a','e','i','o','u'];
Another approach is to use the var keyword, which instructs the compiler to
implicitly type a local variable. Here are some simple examples:
50. var i = 3; // i is implicitly of type int
var s = "sausage"; // s is implicitly of type string
The same principle can be applied to arrays, except that it can be taken one
stage further. By omitting the type qualifier after the new keyword, the
compiler infers the array type:
// Compiler infers char[]
var vowels = new[] {'a','e','i','o','u'};
Here’s how we can apply this to multidimensional arrays:
var rectMatrix = new[,]
{
{0,1,2},
{3,4,5},
{6,7,8}
};
var jaggedMat = new int[][]
{
new[] {0,1,2},
new[] {3,4,5},
new[] {6,7,8,9}
};
Variables and Parameters
A variable represents a storage location that has a modifiable value. A
variable can be a local variable, parameter (value, ref, out, or in), field
(instance or static), or array element.
The Stack and the Heap
The stack and the heap are the places where variables reside. Each has very
different lifetime semantics.
Stack
The stack is a block of memory for storing local variables and parameters.
51. The stack logically grows and shrinks as a method or function is entered and
exited. Consider the following method (to avoid distraction, input argument
checking is ignored):
static int Factorial (int x)
{
if (x == 0) return 1;
return x * Factorial (x-1);
}
This method is recursive, meaning that it calls itself. Each time the method is
entered, a new int is allocated on the stack, and each time the method exits,
the int is deallocated.
Heap
The heap is the memory in which objects (i.e., reference type instances)
reside. Whenever a new object is created, it is allocated on the heap, and a
reference to that object is returned. During a program’s execution, the heap
starts filling up as new objects are created. The runtime has a garbage
collector that periodically deallocates objects from the heap so your program
does not run out of memory. An object is eligible for deallocation as soon as
it’s not referenced by anything that is itself alive.
Value type instances (and object references) live wherever the variable was
declared. If the instance was declared as a field within a class type, or as an
array element, that instance lives on the heap.
NOTE
You can’t explicitly delete objects in C# as you can in C++. An unreferenced object is eventually
collected by the garbage collector.
The heap also stores static fields and constants. Unlike objects allocated on
the heap (which can be garbage-collected), these live until the application
domain is torn down.
52. Definite Assignment
C# enforces a definite assignment policy. In practice, this means that outside
of an unsafe context, it’s impossible to access uninitialized memory.
Definite assignment has three implications:
Local variables must be assigned a value before they can be read.
Function arguments must be supplied when a method is called (unless
marked optional—see “Optional parameters”).
All other variables (such as fields and array elements) are automatically
initialized by the runtime.
For example, the following code results in a compile-time error:
int x; // x is a local variable
Console.WriteLine (x); // Compile-time error
The following, however, outputs 0, because fields are implicitly assigned a
default value (whether instance or static):
Console.WriteLine (Test.X); // 0
class Test { public static int X; } // Field
Default Values
All type instances have a default value. The default value for the predefined
types is the result of a bitwise zeroing of memory and is null for reference
types, 0 for numeric and enum types, '0' for the char type, and false
for the bool type.
You can obtain the default value for any type by using the default
keyword (this is particularly useful with generics, as you’ll see later). The
default value in a custom value type (i.e., struct) is the same as the default
value for each field defined by the custom type:
Console.WriteLine (default (decimal)); // 0
decimal d = default;
53. Parameters
A method can have a sequence of parameters. Parameters define the set of
arguments that must be provided for that method. In this example, the method
Foo has a single parameter named p, of type int:
Foo (8); // 8 is an argument
static void Foo (int p) {...} // p is a parameter
You can control how parameters are passed with the ref, out, and in
modifiers:
Parameter
modifier Passed by
Variable must be definitely
assigned
None Value Going in
ref Reference Going in
in Reference (read-
only)
Going in
out Reference Going out
Passing arguments by value
By default, arguments in C# are passed by value, which is by far the most
common case. This means that a copy of the value is created when it is
passed to the method:
int x = 8;
Foo (x); // Make a copy of x
Console.WriteLine (x); // x will still be 8
static void Foo (int p)
{
p = p + 1; // Increment p by 1
Console.WriteLine (p); // Write p to screen
54. }
Assigning p a new value does not change the contents of x, because p and x
reside in different memory locations.
Passing a reference type argument by value copies the reference but not the
object. In the following example, Foo sees the same StringBuilder
object we instantiated (sb) but has an independent reference to it. In other
words, sb and fooSB are separate variables that reference the same
StringBuilder object:
StringBuilder sb = new StringBuilder();
Foo (sb);
Console.WriteLine (sb.ToString()); // test
static void Foo (StringBuilder fooSB)
{
fooSB.Append ("test");
fooSB = null;
}
Because fooSB is a copy of a reference, setting it to null doesn’t make sb
null. (If, however, fooSB was declared and called with the ref modifier,
sb would become null.)
The ref modifier
To pass by reference, C# provides the ref parameter modifier. In the
following example, p and x refer to the same memory locations:
int x = 8;
Foo (ref x); // Ask Foo to deal
// directly with x
Console.WriteLine (x); // x is now 9
static void Foo (ref int p)
{
p = p + 1; // Increment p by 1
Console.WriteLine (p); // Write p to screen
}
56. Kuin horroksissa tyttö nousi kädet painettuina rintaa vasten ja
silmien kauhistuneina tuijottaessa muutaman askeleen päässä
murisevaan kuonoon. Lattialla makasi neekeritär tainnoksissa. Jospa
voisi herättää hänet tuntoihinsa, ehkäpä he yhdistetyin voimin
saisivat pedon karkoitetuksi.
Jane Porter kumartui, tarttui neekerittären olkapäähän ja ravisteli
häntä kaikin voimin.
"Esmeralda! Esmeralda!" huusi hän. "Auta minua, tai me olemme
molemmat hukassa!"
Esmeralda avasi hitaasti silmänsä. Ensimmäinen, mitä hän näki, oli
nälkäisen naarasleijonan kuolainen kita.
Kauhusta kiljahtaen naispoloinen nousi nelinkontin ja ryömi
huoneen poikki, huutaen: "Oi taivas!"
Hän painoi noin satakaksikymmentä kiloa eikä ollut pystyssä
käydessäänkään mikään gaselli, mutta suorastaan kuvaamattoman
vaikutelman hän nyt herätti kontatessaan ja raahatessaan
suunnattoman lihavaa ruumistaan.
Hetken aikaa leijona oli hiljaa, tuikeasti tarkaten Esmeraldaa,
jonka päämääränä näytti olevan kaappi. Sinne hän koetti sulloa
itseään, mutta kaappi oli liian pieni, hän sai sinne mahtumaan vain
päänsä. Sitten hän päästi huudon, jonka rinnalla viidakon äänet
olivat mitättömiä, ja pyörtyi uudestaan.
Esmeraldan lysähtäessä liikkumattomaksi leijona uudisti
yrityksensä tunkea ruumiinsa sisään murtuneen ristikon puhki.
57. Tyttö seisoi kalpeana ja jähmettyneenä peräseinää vasten ja etsi
kuolemankauhussa jotakin pakopaikkaa. Äkkiä hänen kätensä, joka
tiukasti puristi rintaa, tunsi jotakin kovaa. Se oli revolveri, jonka
Clayton oli hänelle päivemmällä jättänyt.
Nopeasti hän sieppasi sen esiin piilopaikasta, tähtäsi suoraan
leijonan kuonoon ja painoi liipasinta. Salama välähti, kuului
pamahdus, ja sitten peto kiljahti tuskasta ja raivosta. Jane Porter
näki leijonan katoavan ikkunasta, ja sitten hänkin pyörtyi, revolverin
pudotessa hänen viereensä.
Mutta Sabor ei ollut kuollut. Luoti oli vain tehnyt kipeän haavan
toiseen olkaan. Hämmästys, sokaiseva välähdys ja korvia huumaava
pamahdus olivat tosin saaneet sen nopeasti vetäytymään takaisin,
mutta vain hetkeksi.
Pian se oli taas ristikossa, rytistellen sitä entistä raivokkaammin,
mutta samalla tehottomammin, sillä haavoitettu jäsen oli miltei
käyttökelvoton.
Hän näki saaliinsa — molempien naisten — makaavan liikkumatta
lattialla. Heistä ei enää ollut esteitä. Ateria oli Saborin edessä,
tarvitsisi vain vääntää ruumiinsa ristikon läpi.
Hitaasti se tunki suurta ruhoaan tuuma tuumalta aukosta
huoneeseen.
Pian oli pää sisällä, sitten toinen etujalka ja olka!
Varovasti se työnsi sisään haavoittunutta raajaansa, ettei se
sattuisi aukon reunoihin. Hetkinen vielä, ja molemmat hartiat oli
saatu mahtumaan aukosta; pitkä, jäntevä ruumis ja kapeat lanteet
liukuisivat pian perästä.
59. VIIDESTOISTA LUKU
Metsänhaltija
Kuullessaan ampuma-aseen pamahduksen Clayton säikähti ja
aavisti pahaa. Hän luuli aluksi, että joku merimies oli ampunut,
mutta se seikka, että hän oli jättänyt Jane Porterille revolverin, ja
hänen ylen jännittynyt hermostonsa saivat hänet väkisinkin
kuvittelemaan, että neitoa nyt uhkasi jokin suuri vaara, että Janen
kenties juuri tällä hetkellä täytyi puolustaa itseään jotakin villi-
ihmistä tai petoa vastaan.
Mitä hänen kummallinen vangitsijansa tai oppaansa ajatteli, sitä ei
Clayton voinut oikein arvailla, mutta ilmeistä oli, että hänkin oli
kuullut laukauksen ja että se jotenkin vaikutti hänen mieleensä, sillä
hän joudutti heidän matkaansa niin kiivaasti, että Clayton, joka
kompasteli hänen takanaan, turhaan yritti pysyä mukana ja pian jäi
auttamattomasti jälkeen.
Peläten jälleen eksyvänsä hän huusi villi-ihmiselle ja hetken
kuluttua saikin tyydytyksekseen nähdä, kuinka opas puun oksalta
keveästi liukui hänen viereensä.
60. Silmänräpäyksen ajan Tarzan katseli nuorta miestä tiukasti
ikäänkuin epäröiden, mitä olisi paras tehdä. Sitten hän kumartui
Claytonin eteen, osoitti merkeillä, että toisen oli kiedottava
käsivartensa hänen kaulaansa, ja kiipesi sitten valkoinen mies
selässään taas ylös puuhun.
Nyt seuranneita minuutteja ei nuori englantilainen milloinkaan
unohtanut. Korkealla, pitkien taipuvien ja heiluvien oksien teitä, kävi
heidän kulkunsa niin nopeasti, että se tuntui hänestä
uskomattomalta, kun Tarzan sitävastoin oli suutuksissaan
etenemisen hitaudesta.
Notkea olento heittäytyi Claytonia kantaen toiselta huimaavan
korkealta oksalta toiselle tai juoksi varmoin askelin monen
kymmenenkin metrin pituista köynnöskasvien peittämää puunhaaraa
myöten kuin nuorallakävelijä, koko ajan säilyttäen tasapainonsa,
vaikka alla ammotti pimeä syvyys.
Ensimmäisestä hyytävästä pelosta päästyään Clayton alkoi ihailla
ja kadehtia tämän metsänhaltijan jättiläislihaksia ja ihmeteltävää
vaistoa tai harjaannusta, jonka varassa saattoi aarniometsän yön
halki samota yhtä helposti ja turvallisesti kuin Clayton käyskenteli
keskipäivällä pitkin Lontoon katuja.
Toisinaan he tulivat paikalle, missä kasvullisuus oli harvempaa, ja
kuun kirkkaat säteet valaisivat Claytonin kummasteleville silmille sitä
outoa tietä, jota he nyt kulkivat. Silloin hän pidätti henkeään
nähdessään allaan hirvittävät kuilut, sillä Tarzan valitsi aina
mukavimman tien, joka usein oli muutamia kymmeniä metrejä
maanpinnan yläpuolella.
61. Vaikka vauhti tuntuikin nuoresta englantilaisesta erinomaisen
nopealta, liikkui Tarzan nyt verrattain hitaasti, sillä hänen oli aina
etsittävä sellaisia oksia, jotka kestäisivät heidän kaksinkertaisen
painonsa.
Äkkiä he saapuivat aukealle paikalle majan luo. Tarzanin herkkä
korva oli kuullut sen omituisen äänen, joka syntyi, kun Sabor koetti
tunkeutua ristikon läpi, ja niin nopeasti hän laskeutui maahan, että
Claytonista tuntui kuin he olisivat suoraa päätä pudonneet
viidenkymmenen metrin korkeudesta. Alas he kuitenkin pääsivät niin
keveästi, että tuskin lainkaan tärähtivät maata koskettaessaan.
Clayton hellitti nyt otteensa ja näki apinamiehen livahtavan kuin
orava majan toiselle puolelle.
Englantilainen juoksi nopeasti hänen jälkeensä ja tuli paikalle juuri
paraiksi nähdäkseen, kuinka jonkin ison pedon takajalat olivat
menossa ikkunasta sisään. —
Kun Jane Porter jälleen avasi silmänsä ja näki uhkaavan vaaran
kaikessa hirveydessään, heitti hänen urhea nuori sydämensä
viimeisenkin toivon, ja hän alkoi haparoida pudonnutta revolveria
saadakseen tuskattoman lopun, ennenkuin julmat hampaat
repelisivät hänen lihaansa.
Leijona oli ehtinyt tunkea melkein koko ruhonsa huoneeseen,
ennenkuin
Jane löysi aseen ja kohotti sen ohimolleen.
Hetkisen hän viivytteli, kuiskatakseen lyhyen rukouksen luojalleen,
ja silloin hänen silmänsä sattuivat Esmeraldarukkaan, joka tajutonna
mutta silti vielä elossa virui kaapin vieressä.
62. Kuinka hän saattaisi jättää tuon uskollisen olennon armottomien
hampaiden raadeltavaksi? Ei, hänen piti ensin ampua laukaus
tajuttomaan naiseen, ja vasta sitten hän kääntäisi kylmän revolverin
suun itseään kohti.
Kuinka tämä tulikoe häntä vapisutti! Mutta tuhat kertaa
julmempaa olisi ollut antaa tämän rakastavan mustan naisen, joka oli
häntä lapsesta pitäen hoitanut niin hellästi ja huolellisesti kuin
koskaan äiti, jälleen herätä tuntoihinsa kauhean pedon raadeltavana.
Jane Porter hypähti pystyyn ja kiirehti neekerittären viereen. Hän
painoi revolverin suun tiukasti hellää sydäntä vasten, sulki silmänsä
ja —
Sabor päästi hirveän karjaisun.
Tyttö pelästyi, veti liipasinta, kääntyi petoon päin ja samalla
kohotti aseen omaa ohimoaan kohti.
Hän ei laukaissut toista kertaa, sillä hämmästyksekseen hän näki
petoa kiskottavan hitaasti takaisin ikkunasta, ja kuutamossa hän näki
takana kahden miehen päät ja hartiat.
Kun Clayton kiersi majan nurkan ja näki sisään pyrkivän leijonan,
sai hän samalla nähdä, kuinka apinamies molemmin käsin tarttui
pedon pitkään häntään, tuki itseään jaloillaan seinään ja pani kaiken
voimansa liikkeelle kiskoakseen eläimen ulos.
Clayton riensi apuun, mutta apinamies murahti hänelle tuimasti
jotakin, minkä hän käsitti käskyksi, vaikkei voinutkaan sitä
ymmärtää.
63. Heidän molempien ponnistellessa alkoi iso ruho vähitellen liikkua
yhä pitemmälle takaisin ikkunasta, ja silloin Claytonille valkeni, minkä
ripeän urotyön hänen toverinsa oli tehnyt.
Oli todella sankarillista alastoman miehen vetää murisevaa ja
kynsivää leijonaa hännästä ulos ikkunasta pelastaakseen
tuntemattoman valkoisen tytön.
Mitä Claytoniin tulee, niin asia oli toisin, sillä tyttö ei ollut
ainoastaan hänen omaa rotuaan, vaan oli koko maailmassa ainoa
nainen, jota hän rakasti. Vaikka hän tiesi, että leijona tekisi heistä
pian lopun, kiskoi hän sittenkin kuin vimmattu, estääkseen sitä
pääsemästä Jane Porterin kimppuun. Sitten hän muisti äsken
tapahtuneen ottelun apinamiehen ja ison mustaharjaisen leijonan
välillä, ja hänestä alkoi tuntua turvallisemmalta.
Tarzan antoi yhä käskyjään, joita Clayton ei voinut käsittää. Hän
koetti saada typerää valkoista miestä ymmärtämään, että tämän oli
pistettävä myrkytettyjä nuolia Saborin selkään ja kylkiin sekä
iskettävä Tarzanin vyössä riippuvalla pitkällä metsästyspuukolla
pedon sydämeen; mutta toinen ei käsittänyt mitään, eikä Tarzan
uskaltanut päästää otettaan, sillä hän tajusi, ettei hento valkoinen
mies yksinään jaksaisi hetkeäkään pidellä väkevää Saboria.
Hitaasti tuli leijona ikkunasta esille. Vihdoin olivat sen olkapäätkin
ulkona.
Ja silloin Clayton sai nähdä jotakin, mitä ei kukaan ihminen ole
hurjimmissa unissaankaan kokenut. Vaivatessaan aivojaan
keksiäkseen, kuinka yksin suoriutuisi raivostuneesta pedosta, Tarzan
oli äkkiä muistanut ottelunsa Terkozin kanssa. Kun siis isot hartiat
64. tulivat niin paljon esiin ikkunasta, että leijonalla oli enää vain
etukäpälät ikkunalaudalla, hellitti Tarzan äkkiä otteensa.
Nopeasti kuin iskevä kalkkarokäärme hän heittäytyi Saborin
selkään ja notkeat käsivarret kietoutuivat kaulan ympärille "koko
nelsoniin", kuten hän oli oppinut saadessaan verisen voiton
Terkozista. Karjaisten leijona mätkähti selälleen vihollisensa päälle.
Mutta mustatukkainen jättiläinen yhä vain tiukensi puristustaan.
Piesten ilmaa ja maata Sabor riuhtoi ja tempoili päästäkseen
vapaaksi oudosta ahdistajastaan, mutta yhä kireämmäksi kävi
rautainen ote, joka pakotti sen päätä vääntymään keltaisenruskeaa
rintaa kohti.
Yhä ylemmäs liukuivat apinamiehen teräksiset käsivarret Saborin
niskaan, ja sen vastustus heikkeni.
Vihdoin Clayton näki hopeisessa kuunvalossa Tarzanin hartiain ja
käsivarsien valtavien lihasten paisuvan jänteviksi solmuiksi.
Apinamies ponnisti kauan, äärimmäisin voimin — ja rusahtaen
taittuivat Saborin kaulanikamat.
Heti sitten oli Tarzan taas pystyssä, ja toistamiseen tänä päivänä
Clayton kuuli ihmisapinan villin voittohuudon. Kohta kajahti myös
Jane Porterin kauhistunut ääni:
"Cecil — herra Clayton! Voi, mitä se on? Mitä se on?"
Clayton juoksi majan ovelle, huusi, että kaikki oli kunnossa, ja
pyysi häntä aukaisemaan. Tyttö nosti raskaan salvan niin nopeasti
kuin suinkin jaksoi ja melkein veti Claytonin sisään.
65. "Mikä se oli, se hirvittävä karjaisu?" kuiskasi hän painautuen
hänen lähelleen.
"Se oli sen miehen voittohuuto, joka juuri oli pelastanut henkenne,
neiti Porter. Odottakaa, minä tuon hänet tänne, että saatte kiittää
häntä."
Peloissaan Jane ei tahtonut jäädä yksin, vaan seurasi Claytonia
taistelutantereelle, jolla leijonan kuollut ruumis makasi.
Apinain Tarzan oli kadonnut.
Clayton huuteli monta kertaa, mutta kun ei mitään vastausta
kuulunut, palasivat he majaan, missä sentään oli turvallisempaa.
"Se oli hirveä huuto!" sanoi Jane Porter. "Minua värisyttää, kun sitä
vain ajattelenkin. Älkää uskotelko, että niin kauhistava karjunta lähti
ihmiskurkusta."
"Mutta niin on kuitenkin laita, neiti Porter", vastasi Clayton. "Tai
ellei hän ole ihminen, niin sitten hän on metsänhaltija."
Ja hän alkoi kertoa seikkailuistaan tämän oudon olennon kanssa —
kuinka tämä villi ihminen oli kahdesti pelastanut hänen henkensä —
hänen ihmeteltävästä voimastaan, nopeudestaan ja urheudestaan —
hänen ruskeasta ihostaan ja kauniista kasvoistaan.
"En käsitä koko asiaa", lopetti hän. "Ensin luulin häntä Apinain
Tarzaniksi, mutta hän ei puhu eikä ymmärrä englantia, niin että se
oletus ei pidä paikkaansa."
"Olkoon hän kuka tahansa", huudahti tyttö, "mutta häntä me
saamme kiittää siitä, että vielä elämme, ja Jumala siunatkoon ja
66. varjelkoon häntä kauheassa viidakossa".
"Aamen", lisäsi Clayton lämpimästi.
"Siunatkoon, enkös minä olekaan kuollut?"
He kääntyivät ja näkivät Esmeraldan istuvan lattialla silmät
pyöreinä päässä, ikäänkuin ei voisi uskoa näkemäänsä.
Kun Jane Porter oli ollut ampumaisillaan Esmeraldan, oli leijona
juuri silloin karjahtanut, ja se pelasti neekerittären hengen, sillä tyttö
oli vavahtanut, jolloin revolverin suu kääntyi syrjään ja luoti tunki
vahinkoa tekemättä lattiaan.
Nyt laukesi Jane Porterin jännitys, ja hän heittäytyi penkille ja
purskahti kirkuvaan, hysteeriseen nauruun.
67. KUUDESTOISTA LUKU
"Tuiki merkillistä"
Monen kilometrin päässä majasta etelään, hiekkaisella
rantakaistaleella, seisoi kaksi vanhaa miestä väitellen.
Edessä oli aukea Atlantin valtameri, takana mustain maanosa ja
ympärillä viidakon läpitunkematon pimeys.
Villit eläimet karjuivat ja murisivat, kauheita ja pahaenteisiä ääniä
kantautui heidän korviinsa. He olivat kulkeneet pitkiä matkoja
etsiessään leiripaikkaansa, mutta aina menneet väärään suuntaan.
He olivat niin toivottomasti eksyksissä kuin jos äkkiä olisivat
joutuneet toiseen maailmaan.
Tuollaisena hetkenä heidän älynsä joka kipinän täytyi kai kohdistua
ylen tärkeään kysymykseen, josta riippui heidän elämänsä tai
kuolemansa: kuinka he osaisivat takaisin majalle.
Samuel T. Philanderilla oli nyt puheenvuoro.
"Hyvä professori", sanoi hän, "minä olen yhä sitä mieltä, että
elleivät Ferdinand ja Isabella viidennellätoista vuosisadalla olisi
68. voittaneet Espanjan maureja, niin maailma olisi nyt ainakin tuhannen
vuotta edistyneempi. Maurit olivat suvaitsevaisia ja vapaamielisiä
maanviljelijöitä, käsityöläisiä ja kauppiaita — juuri sitä ihmislajia,
joka on luonut Amerikassa ja Euroopassa tapaamamme sivistyksen
— kun sitävastoin espanjalaiset —"
"No, no, herra Philander", keskeytti professori Porter, "heidän
uskontonsahan on ollut ja tulee aina olemaan sen tieteellisen
edistyksen jarruna, joka…"
"Mitä ihmettä, herra professori!" huudahti herra Philander, joka oli
kääntynyt katselemaan viidakkoa, "tuolta näyttää tulevan joku".
Professori Archimedes Q. Porter kääntyi likinäköisen herra
Philanderin osoittamaan suuntaan.
"No no, herra Philander", moitti hän. "Kuinka usein minun
pitääkään kehoittaa teitä keskittämään älylliset kykynne, sillä vain
keskittämällä voitte suunnata järjen korkeimmat voimat niihin
tärkeihin tehtäviin, jotka luonnostaan lankeavat suurten henkien
ratkaistavaksi. Ja nyt rikotte erittäin epähienolla tavalla
kohteliaisuuden alkeita vastaan, kun keskellä oppinutta esitystänne
kiinnitätte huomiota pelkkään nelijalkaiseen Felis-suvun jäseneen.
Kuten sanoin, herra…"
"Taivas varjelkoon, professori, leijonako siellä on?" huusi herra
Philander jännittäen heikkoja silmiään erottaakseen tummaa
troopillista kasvullisuutta vasten kuvastuvan hämäräpiirteisen
hahmon.
"Niin, niin, herra Philander, leijona se on, jos välttämättä tahdotte
käyttää arkikieltä keskustelussanne. Mutta, kuten sanottu…"
69. "Anteeksi, herra professori!" keskeytti taas herra Philander.
"Sallikaa minun huomauttaa, että viidennellätoista vuosisadalla
voitetut maurit epäilemättä ainakin toistaiseksi tyytyvät suuresti
valitettaviin oloihinsa, vaikka me lykkäämmekin keskustelun tuosta
maailmaa kohdanneesta vahingosta siksi, kunnes voimme tuota Felis
carnivoraa ihailla turvallisen välimatkan päästä."
Tällä välin leijona oli rauhallisen arvokkaasti tullut noin kymmenen
askeleen päähän, mihin se seisahtui uteliaana tarkkailemaan
vanhuksia.
Kuutamo valui rannalle, ja outo ryhmä näkyi selvästi keltaista
hiekkaa vasten.
"Tuiki moitittavaa, tuiki moitittavaa!" huudahti professori Porter,
äänessä pieni ärtymyksen värähdys. "En milloinkaan, herra Philander,
en milloinkaan elämässäni ole kuullut, että näitä eläimiä
päästettäisiin juoksentelemaan häkeistään. Varmasti ilmoitan tästä
mieltäkuohuttavasta tapauksesta lähimmän eläintieteellisen
puutarhan johtajille."
"Aivan oikein, professori", sanoi herra Philander, "ja mitä
pikemmin, sitä parempi. Lähtekäämme nyt."
Tarttuen professorin käsivarteen herra Philander läksi siihen
suuntaan, joka nopeammin veisi niin kauas kuin suinkin leijonasta.
He olivat astelleet vain vähän matkaa, kun herra Philander taaksensa
vilkaistessaan huomasi, että leijona seurasi heitä. Hän tarttui
vastustelevaan professoriin tiukemmin ja lisäsi vauhtiaan.
"Kuten sanoin, herra Philander…", toisti professori Porter.
70. Herra Philander vilkaisi jälleen taakseen. Leijonakin oli jouduttanut
askeliaan ja pysytteli itsepintaisesti saman välimatkan päässä heistä.
"Se ajaa meitä takaa!" huohotti herra Philander ja alkoi juosta.
"No no herra Philander", nuhteli professori, "tällainen säädytön
kiire ei mitenkään sovellu oppineille miehille. Mitä ajattelisivatkaan
ystävämme, jos sattumalta näkisivät narrimaisen käytöksemme?
Pyydän, astukaamme vähän arvokkaammin."
Herra Philander vilkaisi taas salavihkaa taakseen.
Voi kauheata! Leijona hypähteli kevyin askelin tuskin viiden jalan
päässä heidän perässään.
Herra Philander päästi professorin käsivarren ja lähti vinhan
laukan, jota ei ammattijuoksijankaan olisi tarvinnut hävetä.
"Kuten sanoin, herra Philander…", huusi professori Porter, kun
hänkin otti jalat alleen, sillä hän oli nähnyt vilahduksen julmista
keltaisista silmistä ja puoliavoimesta kidasta, joka oli perin
tuskastuttavan lähellä hänen persoonaansa.
Liehuvin takinliepein, kiiltävä hattu päässään, professori
Archimedes
Q. Porter pakeni kuutamossa herra Samuel T. Philanderin kintereillä.
Heidän eteensä sattui mäenharja, jolla myös rehoitti viidakon
tiheätä kasvullisuutta, ja sinne, suojaavien puiden turvaan herra
Samuel T. Philander nyt suuntasi ällistyttävän tarmokkaat
loikkauksensa, mutta samalla tarkkasi sieltä lehvien lomasta kaksi
tiukkaa silmää uteliaina tätä kilpajuoksua.
71. Siellä oli Apinain Tarzan virnistellen katselemassa merkillistä
leskisilläoloa.
Hän ymmärsi, että leijonan puolesta nuo kaksi vanhusta saivat olla
kutakuinkin rauhassa. Jo se seikka, että Numa oli päästänyt näin
helpon saaliin kynsistään, ilmaisi Tarzanille, että pedon maha oli
täynnä.
Se saattaisi ehkä ajaa heitä, kunnes taas tulisi nälkä, mutta
otaksuttavampaa oli, että ellei sitä ärsytettäisi, se pian väsyisi leikkiin
ja vetäytyisi viidakkoluolaansa.
Pahimpana vaarana oli muuten se, että jompikumpi miehistä ehkä
kompastuisi ja kaatuisi, sillä silloin keltainen pahahenki olisi heti
käynyt onnettomaan käsiksi, voimatta hillitä murhanhimoaan.
Tarzan laskeutui siis kiireesti sen puun alaoksalle, jota pakolaiset
lähenivät, ja kun herra Samuel T. Philander tuli huohottaen ja
hengästyneenä eikä enää olisi jaksanut kiivetä turvaan oksalle,
kumartui Tarzan, tarttui hänen takinkaulukseensa ja nosti hänet
viereensä istumaan.
Seuraavassa hetkessä joutui professori saman ystävällisen
käsittelyn alaiseksi ja pääsi suojaan, karjuvan Numan juuri loikatessa
saadakseen kynsiinsä saaliin, joka oli siltä niin odottamatta siepattu.
Hetken aikaa molemmat miehet puuskuttivat oksalla Tarzanin
nojatessa selkäänsä runkoon ja tarkastellessa heitä puolittain
uteliaana, puolittain huvitettuna.
Puheenvuoron otti aluksi professori.
72. "Minuun koskee kovasti, herra Philander, että olette osoittanut
sellaista miehekkyyden puutetta alempaan luomakuntaan kuuluvan
olion edessä ja pelollanne saanut minutkin kiihtymään. Minun täytyy
siis alusta saakka ryhtyä kantaani selittämään. Kuten siis sanoin,
herra Philander, kun minut keskeytitte…"
"Professori Archimedes Q. Porter", tarttui puheeseen herra
Philander jääkylmästi, "nyt on menty niin pitkälle, että kärsivällisyys
olisi rikos, joka on vain verhottu hyveen viittaan. Te olette syyttänyt
minua pelkuruudesta. Näytätte vihjaisevan, että juoksitte vain
saadaksenne minut kiinni ettekä pelastuaksenne leijonan kynsistä.
Olkaa varuillanne, professori Arkhimedes Q. Porter! Minä olen
epätoivoon joutunut mies. Minun kärsivällisyyteni mitta alkaa olla
täysi."
"No no, herra Philander, no, no!" tyynnytti professori Porter. "Te
unohdatte säädyllisyyden."
"En unohda vielä mitään, professori Archimedes Q. Porter, mutta
uskokaa minua, herra, että pian voisin unohtaa huomattavan
asemanne tieteellisessä maailmassa ja harmaat hiuksenne."
Professori istui vaiti muutaman minuutin, ja pimeys peitti sen
katkeran hymyn, joka väreili hänen kurttuisilla kasvoillaan. Äkkiä hän
virkkoi:
"Kuulkaas nyt, Philander, jos te haluatte tapella, ottakaa takki
päältänne ja tulkaa alas maahan, ja minä annan teille päihin yhtä
nokkelasti kuin kuusikymmentä vuotta sitten Läski-Evansin aitan
takana."
73. "Archie!" läähätti hämmästynyt herra Philander. "Kuinka hyvältä
tuo kuuluu! Kun käyttäydytte ihmisen tavoin, pidän teistä paljon,
mutta parinakymmenenä viime vuonna on näyttänyt siltä kuin olisitte
melkein unohtanut inhimillisen luonnon."
Professori ojensi laihan, vapisevan käden ja hapuili pimeässä,
kunnes tapasi vanhan ystävänsä olan.
"Suokaa anteeksi, Samuel", sanoi hän hiljaa. "Siitä ei ole vielä
täyteen kahtakymmentä vuotta, ja Jumala yksin tietää, kuinka
kovasti olen koettanut olla inhimillinen Janen ja myöskin teidän
tähtenne senjälkeen, kun Hän otti toisen Janeni luokseen."
Toinen vanha käsi hiipi herra Philanderin sivulta ja tarttui hänen
olallaan lepäävään käteen, eivätkä mitkään sanat olisi paremmin
tulkinneet heidän sydämiensä ajatuksia.
Vähään aikaan he eivät puhuneet mitään. Leijona asteli heidän
alapuolellaan levottomana edestakaisin. Puuhun noussut kolmas
olento oli kätkössä tiheiden lehvien takana. Hänkin oli vaiti —
liikkumatta kuin veistos.
"Te veditte minut puuhun todellakin juuri parahiksi", sanoi
professori vihdoin. "Tahdon kiittää teitä. Te pelastitte henkeni."
"Mutta enhän minä vetänyt teitä tänne, professori", vastasi herra
Philander. "Kaikkea vielä! Kiihtymys sai minut kokonaan unohtamaan,
että minutkin joku veti tänne — joku tai joitakuita on varmaankin
tässä samassa puussa."
"Mitä?" huudahti professori Porter. "Oletteko asiasta aivan varma,
herra Philander?"
74. "Ihan varma, professori", vakuutti herra Philander. "Ja olen sitä
mieltä, että meidän pitää häntä kiittää. Ehkä hän istuu ihan
vieressämme, professori?"
"Mitä? Kuinka niin? No no, herra Philander, no no!" sanoi
professori
Porter siirtyen lähemmäksi herra Philanderia.
Juuri silloin johtui Apinain Tarzanin mieleen, että Numa oli
liikuskellut kyllin kauan puun juurella, ja siksi hän kohotti nuorta
päätänsä taivasta kohden, jolloin kahden vanhuksen pelästyneihin
korviin kajahti ihmisapinan hirveä varoitushuuto.
Molemmat ystävykset vapisivat epävakaisella oksallaan. He näkivät
leijonan lopettavan väsymättömän tepastelunsa, kun se kuuli tämän
vertahyytävän huudon, ja sitten nopeasti luikkivan tiehensä
viidakkoon, jonne se tuota pikaa katosi.
"Leijonakin vapisee pelosta", kuiskasi herra Philander.
"Tuiki merkillistä, tuiki merkillistä!" mumisi professori Porter
tarttuen kouristuneesti herra Philanderiin, päästäkseen jälleen
tasapainoon, jonka oli äkillisessä pelästyksessä menettänyt.
Molempien onnettomuudeksi ei herra Philanderin tasapainotila tällä
hetkellä ollut yhtään parempi, joten pieni sysäys ja professori
Porterin ruumiin paino saivat hänet pahasti horjahtamaan. Hetken
aikaa he heilahtelivat sinne tänne ja sitten, päästäen oppineille
miehille erittäin sopimattomia huutoja, mätkähtivät suinpäin oksalta
maahan, pidellen toisistaan kiinni.
Kului joitakin hetkiä, ennenkuin kumpikaan liikahti, sillä he olivat
varmat siitä, että yksikin sellainen yritys saisi heidät tuntemaan kipua
75. monen monista niukahduksista ja luunmurtumista, jotka tekisivät
kaiken liikkumisen mahdottomaksi.
Vihdoin professori Porter koetti liikuttaa toista säärtään. Hänen
hämmästyksekseen se totteli hänen tahtoaan kuin ennenkin. Silloin
hän koukisti toistakin jalkaansa ja ojensi sen jälleen suoraksi.
"Tuiki merkillistä, tuiki", jupisi hän.
"Jumalan kiitos, professori", kuiskasi herra Philander kiihkeästi,
"ette siis ole kuollut?"
"No no, herra Philander, no no", rauhoitti professori Porter. "Oikein
varmaa se ei vielä ole."
Raskas huoli sydämessä professori Porter ojensi oikean
käsivartensa. Mikä ilo! Se ei ollut loukkaantunut. Hengitystään
pidättäen hän sitten heilautti vasenta käsivarttaan runnellun
ruumiinsa yllä — se liikkui!
"Tuiki merkillistä, tuiki merkillistä", sanoi hän.
"Kenelle lähetätte merkkejä, professori?" kysyi herra Philander
hämmästyneenä.
Professori Porter ei suvainnut vastata mitään tähän lapselliseen
kysymykseen. Sensijaan hän nosti hellävaroen päänsä maasta ja
nyökäytteli sitä viitisen kertaa edestakaisin.
"Tuiki merkillistä", mumisi hän. "Sekään ei ole loukkaantunut."
Herra Philander ei ollut liikkunut paikaltaan; hän ei ollut uskaltanut
yrittääkään. Kuinka ihminen voisikaan liikkua, kun kädet, jalat ja
76. selkä olivat poikki?
Hänen toinen silmänsä oli hautautunut pehmeään liejuun, toisella
hän tirkisteli professori Porterin kummallisia temppuja.
"Kuinka surullista!" sanoi herra Philander puoliääneen.
"Aivotärähdys on vienyt häneltä järjen! Kuinka surullista todellakin,
niin nuori kuin hän vielä on!"
Professori Porter käännähti vatsalleen, koukisti varovasti
selkäänsä, kunnes oli ison uroskissan kaltainen haukkuvan koiran
sitä ahdistaessa. Sitten hän suoristautui ja tunnusteli tomumajansa
eri osia.
"Kaikki kunnossa!" huudahti hän. "Tuiki merkillistä!"
Nyt hän nousi ja luoden silmäyksen herra Samuel T. Philanderin
yhä liikkumattomaan hahmoon sanoi:
"No no, herra Philander, ei nyt sovi loikoa ja kuhnailla. Meidän on
noustava ja ryhdyttävä hommiin."
Herra Philander nosti mutaan painuneen toisen silmänsä ja
katsahti sanattomassa raivossa professori Porteriin. Sitten hän koetti
nousta. Eikä kukaan olisi voinut enemmän hämmästyä kuin hän itse,
sillä jo ensi yritys onnistui täydellisesti.
Kuitenkin häntä vielä kuohutti professori Porterin väärä syytös, ja
hän oli juuri antaa kireän vastauksen, kun samassa huomasi heistä
jonkun askeleen päässä seisovan oudon olennon, joka tähysteli heitä
tarkasti.
77. Professori Porter oli löytänyt silkkihattunsa, harjannut sitä
huolellisesti takkinsa hihalla ja pannut sen taas päähänsä.
Huomatessaan herra Philanderin viittaavan johonkin takanaan
olevaan hän kääntyi ja näki edessään liikkumatonna seisovan
jättiläisen, joka oli ihan alasti, ellei oteta lukuun lannevaatetta ja
muutamia metallikoristeita.
"Hyvää iltaa, herra!" sanoi professori kohauttaen hattuaan.
Vastaukseksi jättiläinen viittasi heitä seuraamaan ja lähti sitten
pitkin rantaa sinne päin, mistä he juuri olivat tulleet. "Luulen, että on
viisainta seurata häntä", sanoi herra Philander.
"No no, herra Philander", vastasi professori, "hetki sitten te esititte
mitä loogillisimpia todistuksia tukeaksenne väitettänne, että leiri oli
meistä suoraan etelään. Aluksi epäilin, mutta vihdoin saitte minut
uskomaan, ja nyt olen varma, että etelään meidän on mentävä
päästäksemme ystäviemme luo. Siksipä minä lähden edelleenkin
etelään."
"Mutta, professori Porter, tämä mies voi tietää asian paremmin
kuin kumpikaan meistä. Hän näyttää olevan täältä syntyisin.
Seuratkaamme häntä edes vähän matkaa."
"No no, herra Philander", huomautti taas professori, "toisen on
vaikea saada mitään minun päähäni, mutta kun se kerran on
tapahtunut, pysyy se siellä peruuttamatta. Minä jatkan siis siihen
suuntaan, jonne itse tahdon, vaikkapa minun pitäisi perille
päästäkseni kulkea koko Afrikan ympäri."
Väittelyn keskeytti Tarzan, joka palasi heidän luokseen, nähtyään,
etteivät he seuranneet häntä.
78. Taas hän antoi heille merkkejä, mutta he yhä kiistelivät.
Silloin apinamies menetti malttinsa heidän typeryytensä vuoksi.
Hän tarttui pelästynyttä herra Philanderia olkaan ja ennenkuin tämä
arvoisa herra tiesi, aiottiinko hänet tappaa vai ainoastaan runnella
raajarikoksi, oli Tarzan sitonut köytensä toisen pään tukevasti herra
Philanderin kaulaan.
"No no, herra Philander", moitti professori Porter, "teidän
arvollenne ei mitenkään sovi, että alistutte noin häpeälliseen
kohteluun."
Mutta tuskin olivat sanat päässeet hänen suustaan, kun häneen
itseensäkin tartuttiin ja hänenkin kaulansa sidottiin samaan köyteen.
Sitten Tarzan lähti matkalle pohjoiseen, laahaten mukanaan pahasti
säikähtynyttä professoria ja tämän sihteeriä.
Syvän hiljaisuuden vallitessa marssivat nämä kaksi vanhusta, ja
kun olivat väsyneitä ja toivottomia, tuntui matka heistä kestävän
tunteja; mutta pienelle mäelle tultaessa yllätti heidät ilo, sillä
edessään, tuskin sadan metrin päässä, he näkivät majan.
Silloin Tarzan päästi heidät vapaiksi ja osoittaen pikku rakennusta
katosi läheiseen viidakkoon.
"Tuiki merkillistä, tuiki merkillistä!" huohotti professori. "Mutta nyt
näette, herra Philander, että olin oikeassa, kuten tavallisesti. Jos ette
olisi ollut niin itsepäinen, olisimme säästyneet monilta nöyryyttäviltä,
etten sanoisi vaarallisilta tapahtumilta. Pyydän, että tästälähin
annatte kypsyneemmän ja käytännöllisemmän hengen johtaa
itseänne, kun viisaat neuvot ovat tarpeen."
79. Herra Samuel T. Philander oli niin hyvillään seikkailun onnellisesta
päättymisestä, ettei loukkaantunut professorin pistosanoista. Hän
vain tarttui ystävänsä käsivarteen ja hoputti häntä majalle päin.
Suuri oli haaksirikkoisten ilo, kun heidän pieni joukkonsa taas oli
koolla. Vielä päivän sarastaessa he kertoivat seikkailujaan ja
lausuivat arveluita kummallisesti vartijastaan ja suojelijastaan, jonka
olivat tällä autiolla rannalla tavanneet.
Esmeralda uskoi varmasti, ettei se ollut kukaan muu kuin Jumalan
enkeli, joka oli erityisesti lähetetty heistä huolta pitämään.
"Jos olisitte nähnyt hänen syövän leijonan raakaa lihaa,
Esmeralda", sanoi Clayton nauraen, "niin olisitte varmaan pitänyt
häntä jokseenkin aineellisena enkelinä".
"Liha ei muuta asiaa, massa Clayton", vastasi Esmeralda, "mutta
luulen, että Herra on unohtanut antaa hänelle mukaan tulitikkuja,
kun hänet pantiin niin kovalla kiireellä lähtemään avuksemme. Eikä
kukaan voi ilman tikkuja keittää."
"Hänen äänessään ei myöskään ollut mitään taivaallista", virkkoi
Jane Porter hiukan väristen, kun muisti leijonan tappamista
seurannutta hirveää karjuntaa.
"Ei myöskään soveltuisi minun käsitykseeni jumalaisista
sanansaattajista", huomautti professori Porter, "se seikka, että tuo —
hm herrasmies sitoi kaksi kunnioitettavaa oppinutta vierekkäin ja
laahasi heitä viidakon halki, ikäänkuin he olisivat olleet härkiä."
80. SEITSEMÄSTOISTA LUKU
Hautajaisia
Kun päivä nyt oli valjennut, alkoi pieni seurue, josta kukaan ei ollut
syönyt eikä nukkunut edellisestä aamusta saakka, hommata itselleen
ruokaa.
Arrowin kapinoitsijat olivat tuoneet maihin pienen varaston
kuivattua lihaa, säilykelientä ja vihanneksia, korppuja, vehnäjauhoja,
teetä ja kahvia näille viidelle, jotka he olivat muuten jättäneet oman
onnensa nojaan ja jotka nyt kiiruhtivat tyydyttämään nälkäänsä
pitkällisen paaston jälkeen.
Seuraavana toimena oli saattaa maja asuttavaan kuntoon, ja siksi
päätettiin heti poistaa hirveät muistot siitä murhenäytelmästä, joka
siellä oli jolloinkin menneinä päivinä tapahtunut.
Professori Porter ja herra Philander olivat syventyneet tutkimaan
luurankoja. Molempien isompien he ilmoittivat kuuluneen valkoisen
rodun miehelle ja naiselle.
81. Pientä luurankoa tarkastettiin vain ohimennen. Kun se oli
kehdossa, saattoi jo siitäkin päättää, että se oli ollut onnettoman
pariskunnan lapsi.
Kun isompaa luurankoa sovitettiin hautauskuntoon, löysi Clayton
raskaan kultasormuksen, joka ilmeisesti oli ollut miehen sormessa
hänen kuollessaan, sillä sormen hoikka luu oli vielä tuossa kultaisen
korun ympäröimänä. Otettuaan sen käteensä lähemmin
tarkastettavakseen Clayton huudahti hämmästyksestä, sillä
sormuksessa oli Greystoke-suvun vaakuna.
Samaan aikaan Jane Porter tapasi kaapista kirjoja ja avatessaan
erään nimilehden näki kirjoituksen John Clayton, Lontoo. Toisessa
kirjassa, jota hän heti sen jälkeen tutki, oli vain nimi Greystoke.
"Herra Clayton!" huudahti hän, "mitä tämä merkitsee? Näissähän
on jonkun sukulaisenne nimi!"
"Ja tässä", vastasi Clayton vakavana, "on Greystoke-suvun suuri
sormus, joka on ollut hukassa siitä asti kuin setäni John Clayton,
entinen loordi Greystoke, katosi jäljettömiin. Luultiin hänen
hukkuneen mereen."
"Mutta kuinka selitätte, että nämä esineet ovat joutuneet tänne
Afrikan villiin viidakkoon?"
"Siihen on vain yksi selitys mahdollinen, neiti Porter", vastasi
Clayton. "Setäni ei hukkunut, vaan kuoli tässä majassa, ja tuossa
lattialla on kaikki, mitä hänestä on jäljellä."
"Silloin on tämä varmaankin ollut lady Greystoke", sanoi Jane
Porter totisena, osoittaen vuoteelle jäänyttä luurankoa.
82. "Niin juuri, se kaunis lady Alice", vastasi Clayton, "jonka monista
hyveistä ja persoonallisesta viehätysvoimasta olen usein kuullut äitini
ja isäni puhuvan. Onneton naisparka!" mutisi hän surullisena.
Hartaan ja juhlallisen tunnelman vallitessa haudattiin loordi ja lady
Greystoken luurangot heidän afrikkalaisen majansa edustalle, ja
heidän keskelleen laskettiin Kaala-apinan lapsen hennot luut.
Sovitellessaan lapsen pieniä luita purjekankaan palaseen alkoi
herra Philander äkkiä tarkastella kalloa yksityiskohtaisesti. Sitten hän
kutsui luokseen professori Porterin, ja molemmat keskustelivat
matalalla äänellä hyvän aikaa.
"Tuiki merkillistä, tuiki merkillistä", sanoi professori Porter.
"Meidän pitää heti ilmoittaa herra Claytonille, mitä olemme
huomanneet!"
"No no, herra Philander, no no!" väitti vastaan professori
Archimedes
Q. Porter. "Annetaan kuolleiden olla rauhassa."
Ja sitten tämä valkohapsinen vanhus luki hautajaissanat tämän
merkillisen haudan partaalla, neljän seuralaisensa seisoessa
kumartuneina ja paljain päin hänen ympärillään.
Korkealla puussa lymyten tarkkasi Apinain Tarzan tätä juhlallista
toimitusta, mutta erityisesti kiintyi hänen huomionsa Jane Porterin
suloisiin kasvoihin ja siroon vartaloon.
Hänen yksinkertaisessa, kesyttömässä sydämessään heräsi uusia
tunteita. Hän ei voinut niitä itsekään ymmärtää. Hän ei tiennyt, miksi
hänen teki niin kovasti mielensä katsella näitä ihmisiä tai miksi hän
83. oli nähnyt sellaista vaivaa pelastaakseen nuo kolme miestä. Mutta
sen hän käsitti hyvinkin, miksi hän oli estänyt Saborin raatelemasta
vieraan tytön hentoa ruumista.
Miehet olivat kaiketi tyhmiä, naurettavia ja pelkureita. Marakatti
Manukin oli heitä älykkäämpi. Jos nämä olivat hänen omaa rotuaan,
niin ei todella ollut syytä ylpeilyyn.
Mutta tyttö, ah — se oli aivan toinen asia. Tosin Tarzan ei osannut
sitä itselleen selittää, mutta hän tajusi, että tyttö oli luotu
suojeltavaksi, ja että hän itse oli luotu tytön suojelijaksi.
Hän kummasteli, miksi he olivat kaivaneet maahan suuren kuopan,
haudatakseen vain vanhoja luita. Siinä ei varmastikaan ollut järkeä;
eihän kukaan tahtonut varastaa vanhoja luita. Jos niissä olisi ollut
lihaa, olisi hän ymmärtänyt, sillä vain maahan kätkemällä voi
saalistaan suojella Dangolta, hyeenalta, ynnä muilta viidakon
ryöväreiltä.
Kun hauta oli luotu umpeen, lähti seurue takaisin majalle päin, ja
Esmeralda vuodatti yhä runsaasti kyyneleitä niiden kahden vuoksi,
joista hän ei ollut ennen kuullut ja jotka olivat kuolleet
kaksikymmentä vuotta sitten. Mutta äkkiä hän sattui katsahtamaan
satamaan päin ja hänen kyyneleensä kuivuivat heti.
"Katsokaa noita kelvottomia!" huusi hän viitaten Arrowia kohti.
"Tuolla ne menee tiehensä ja me jäädään tälle kirotulle saarelle."
Ja totta olikin, että Arrow hiljalleen lipui sataman suusta aavalle
merelle.
84. "He lupasivat jättää meille kiväärejä ja ampumatarpeita",
huomautti
Clayton. "Ne viheliäiset otukset."
"Se on varmaankin sen miehen ansiota, jota he sanovat
Snipesiksi", sanoi Jane Porter. "King oli lurjus, mutta sentään jonkun
verran inhimillinen. Jollei häntä olisi surmattu, niin uskon varmasti,
että hän olisi pitänyt meistä huolta ja meille olisi toimitettu kaikkia
tarpeita, ennenkuin meidät jätettiin oman onnemme nojaan."
"Olen pahoillani, etteivät he ennen lähtöään käyneet luonamme",
sanoi professori Porter. "Olisin pyytänyt heitä jättämään aarteen
meille, sillä jos se hukkuu, niin joudun taloudellisesti perikatoon."
Jane Porter katsoi isäänsä surullisesti.
"Siitä ei olisi ollut apua, rakas isä", vastasi hän, "sillä juuri aarteen
vuoksi he tappoivat päällystönsä ja jättivät meidät tähän hirveään
erämaahan".
"No no, lapsi, no no!" muistutti professori Porter. "Sinä olet hyvä
tytär, mutta kokematon käytännöllisissä asioissa." Ja sitten hän
kääntyi ja lähti verkalleen astelemaan viidakkoa kohti, kädet selän
takana pitkän takin liepeiden alla ja silmät kiinni maassa.
Hänen tyttärensä katsoi hänen jälkeensä surumielisesti hymyillen,
kääntyi sitten herra Philanderin puoleen ja kuiskasi:
"Olkaa niin ystävällinen ja estäkää häntä lähtemästä omille
retkilleen kuten eilen. Kuten tiedätte, luotamme teihin, että pidätte
häntä tarkasti silmällä."
85. "Hän käy päivä päivältä yhä kiusallisemmaksi", vastasi herra
Philander huokaisten ja päätään pudistaen. "Luulenpa, että hän nyt
on menossa eläintieteelliseen puutarhaansa ilmoittamaan, että
eilisiltana yksi heidän leijonistaan oli päässyt irti. Oh, neiti Jane, te
ette tiedä, mitä kaikkea saan kestää…"
"Tiedänpä kylläkin, herra Philander, mutta vaikka me kaikki häntä
rakastamme, te yksin kykenette häntä ohjaamaan, sillä mitä tahansa
hän teille mahdollisesti sanookin, hän kunnioittaa teidän suurta
oppianne, ja siksi hänellä on tavaton luottamus teidän
mielipiteisiinne. Isä-parka ei edes osaa tehdä eroitusta
oppineisuuden ja viisauden välillä."
Herra Philander näytti joutuvan hieman hämilleen näistä sanoista
ja lähti seuraamaan professori Porteria, mielessään pohtien pitikö
hänen käsittää imarteluksi vai loukkaukseksi neiti Porterin
kaksimielinen kohteliaisuus.
Tarzan oli pannut merkille sen hämmennyksen ja levottomuuden,
joka oli ilmennyt majan uusien asukkaiden kasvoilla, kun he
huomasivat Arrow-laivan lähdön. Hän päätti siis, kun laiva lisäksi oli
hänelle merkillinen uutuus, kiirehtiä sataman suun pohjoispuoliseen
kärkeen sitä lähemmin katsellakseen ja samalla ottaakseen selville,
minne päin se suuntasi kulkunsa.
Liikkuen nopeasti puusta puuhun hän saapui niemeen vain
hetkistä myöhemmin kuin laiva oli päässyt ulos satamasta, ja niinpä
hänellä olikin erinomainen tilaisuus tarkastella oudon, uivan talon
ihmeitä.
Maan puolelta puhalsi heikko tuuli, ja alus, joka oli verkalleen
kulkenut ulos satamasta vain parin purjeen varassa, levitti nyt kaikki
86. mahdolliset riepunsa päästäkseen merelle niin nopeasti kuin suinkin.
Tarzan katseli ihastuneena aluksen siroja liikkeitä ja olisi hartaasti
suonut olevansa sen kannella. Äkkiä hänen tarkat silmänsä
huomasivat vähäpätöisen savujuovan kaukana pohjoisella
taivaanrannalla, ja hän ihmetteli, mistä sellainen sai alkunsa aavalla
merellä.
Melkein samaan aikaan sen huomasi myöskin Arrowin tähystäjä, ja
Tarzan näki, kuinka purjeita heti muutettiin ja vähennettiin. Alus
kääntyi ja lähti pyrkimään takaisin maata kohden. Eräs mies seisoi
keulassa ja heitti alinomaan mereen köyttä, jonka päässä oli jokin
pieni esine. Tarzan ihmetteli, mitä tuokin homma tarkoitti.
Vihdoin laiva kääntyi vasten tuulta, ankkuri laskettiin ja purjeet
päästettiin alas. Kannella häärättiin kovasti. Aluksesta laskettiin
vesille vene ja siihen sijoitettiin iso arkku. Sitten kymmenkunta
merimiestä tarttui airoihin ja souti sitä niemenkärkeä kohti, missä
Tarzan kyyristeli puun lehvien keskellä. Veneen tullessa lähemmäksi
Tarzan näki, että sen perässä istui rottanaama.
Pari minuuttia vielä, ja vene tuli rantaan. Miehet hyppäsivät maihin
ja nostivat ison arkun hietikolle. He olivat niemen pohjoisella
rannalla, niin ettei majasta päin voitu heitä nähdä.
Miehet väittelivät vähän aikaa kiivaasti. Sitten rottanaama astui
muutamien kumppanien kera sille kummulle, jolla Tarzanin
kätköpaikka oli. He katselivat tutkivasti ympärilleen.
"Tässä on hyvä paikka", sanoi rottanaamainen merimies, osoittaen
Tarzanin puun alustaa.
87. "Se on yhtä hyvä kuin joku toinenkin", vastasi eräs hänen
kumppaneistaan. "Jos aarre löydetään laivasta, niin se kuitenkin
takavarikoidaan. Yhtä hyvin hautaamme sen tänne siltä varalta, että
joku meistä välttää hirsipuun, hakee sen täältä ja saa siitä nauttia."
Rottanaama kutsui sitten veneen luo jääneitä miehiä, ja nämä
tulivat hitaasti kuokkineen ja lapioineen.
"Pitäkää kiirettä!" huusi Snipes.
"Älä rähise!" vastasi eräs miehistä happamesti. "Et sinä mikään
amiraali ole, senkin krapu."
"Mutta nyt minä olen kapteeni, pidä se muistissasi, tolvana!" huusi
Snipes, päästäen suustaan kauheita kirouksia.
"Älkää nyt pojat, riidelkö", varoitti eräs, joka ei vielä ollut puhunut.
"Mitä apua siitä on, että alamme keskenämme kinastella?"
"Oikein", vastasi eräs merimies, joka oli ruvennut vastustamaan
Snipesin itsevaltaista käytöstä, "mutta ei tässä sakissa kannata
kenenkään nokkaansa nostella".
"Kaivakaa, pojat, tästä", sanoi Snipes osoittaen paikkaa puun
juurella. "Ja sillä aikaa saa Peter piirtää paikasta kartan, jotta
löydämme sen jälkeenpäin. Tom ja Bill, ottakaa pari miestä avuksi ja
tuokaa arkku tänne."
"Mitäs sinä sitten aiot tehdä?" kysyi äskeinen riitelijä. "Sinäkö vain
komentelet?"
"Tee sinä vain työtä", murisi Snipes. "Et kai luule, että kapteeni
tarttuisi lapioon, vai mitä?"
88. Kaikki miehet katsoivat häneen kiukkuisina. Yksikään ei pitänyt
Snipesistä, ja hänen mahtailunsa sen jälkeen, kun hän oli murhannut
kapinoitsijain todellisen johtajan, Kingin, oli vain antanut uutta
virikettä heidän vihalleen.
"Tarkoitatko, ettet aio ottaa lapiota ja auttaa työssä? Ei kai sinun
olkasi niin penteleen kipeä ole", sanoi Tarrant, se merimies, joka
ensin oli hänelle puhunut.
"En, vaikka lempo olisi", vastasi Snipes, hermostuneesti hypistellen
revolverin perää.
"Silloin hitto vie, saat ainakin kuokan, ellei sinulle lapio kelpaa…"
Näin sanoen Tarrant kohotti kuokkansa ja vimmatusti iskien hautasi
sen kärjen Snipesin aivoihin.
Hetken aikaa miehet seisoivat ääneti ja katselivat kumppaninsa
raivon uhria. Sitten yksi heistä virkkoi:
"Oikein tehty sille öykkärille!"
Eräs toinen iski kuokkansa maahan, mutta kun multa oli pehmeää,
heitti hän kuokan ja tarttui lapioon, minkä jälkeen toisetkin ryhtyivät
työhön. Murhasta ei sen enempää puhuttu, mutta miesten mieli oli
keveämpi kuin ennen Snipesin komentoaikana.
Kun kuoppa oli kaivettu arkun suuruiseksi, ehdotti Tarrant, että
sitä laajennettaisiin ja Snipesin ruumis haudattaisiin arkun päälle.
"Se eksyttäisi ketä tahansa, joka rupeaisi täällä kaivelemaan",
selitti hän.
89. Welcome to our website – the perfect destination for book lovers and
knowledge seekers. We believe that every book holds a new world,
offering opportunities for learning, discovery, and personal growth.
That’s why we are dedicated to bringing you a diverse collection of
books, ranging from classic literature and specialized publications to
self-development guides and children's books.
More than just a book-buying platform, we strive to be a bridge
connecting you with timeless cultural and intellectual values. With an
elegant, user-friendly interface and a smart search system, you can
quickly find the books that best suit your interests. Additionally,
our special promotions and home delivery services help you save time
and fully enjoy the joy of reading.
Join us on a journey of knowledge exploration, passion nurturing, and
personal growth every day!
ebookbell.com