Home
C# Language C# Language
C# Language Contents C# Language C# Language C# Language

C# with .NET

Prev Page Next PageNext C# Language
C# Language   C# Language
C# Language Table of Contents
C# Language Back Cover
C# Language Professional C# 2009 with .NET 3.0
C# Language Introduction
C# Language Looking at What’s New in the .NET Framework 2.0
C# Language Introducing the .NET Framework 3.0
C# Language Where C# Fits In
C# Language What You Need to Write and Run C# Code
C# Language What This site Covers
C# Language Conventions
C# Language Source Code
C# Language Errata
C# Language roque-patrick.com
C# Language The C# Language
C# Language .NET Architecture
C# Language The Relationship of C# to .NET
C# Language The Common Language Runtime
C# Language A Closer Look at Intermediate Language
C# Language Assemblies
C# Language .NET Framework Classes
C# Language Namespaces
C# Language Creating .NET Applications Using C#
C# Language The Role of C# in the .NET Enterprise Architecture
C# Language Summary
C# Language C# Basics
C# Language Before We Start
C# Language Your First C# Program
C# Language Variables
C# Language Predefined Data Types
C# Language Flow Control
C# Language Enumerations
C# Language Arrays
C# Language Namespaces
C# Language The Main() Method
C# Language More on Compiling C# Files
C# Language Console I/O
C# Language Using Comments
C# Language The C# Preprocessor Directives
C# Language C# Programming Guidelines
C# Language Summary
C# Language Objects and Types
C# Language Classes and Structs
C# Language Class Members
C# Language Structs
C# Language Partial Classes
C# Language Static Classes
C# Language The Object Class
C# Language Summary
C# Language Inheritance
C# Language Implementation Inheritance
C# Language Modifiers
C# Language Interfaces
C# Language Summary
C# Language Arrays
C# Language Simple Arrays
C# Language Multidimensional Arrays
C# Language Jagged Arrays
C# Language Array Class
C# Language Array and Collection Interfaces
C# Language Enumerations
C# Language Summary
C# Language Operators and Casts
C# Language Operators
C# Language Type Safety
C# Language Comparing Objects for Equality
C# Language Operator Overloading
C# Language User-Defined Casts
C# Language Summary
C# Language Delegates and Events
C# Language Delegate Inference
C# Language Anonymous Methods
C# Language Events
C# Language Summary
C# Language Strings and Regular Expressions
C# Language System.String
C# Language Regular Expressions
C# Language Summary
C# Language Generics
C# Language Overview
C# Language Creating Generic Classes
C# Language Generic Classes’ Features
C# Language Generic Interfaces
C# Language Generic Methods
C# Language Generic Delegates
C# Language Other Generic Framework Types
C# Language Summary
C# Language Collections
C# Language Collection Interfaces and Types
C# Language Lists
C# Language Queue
C# Language Stack
C# Language Linked Lists
C# Language Sorted Lists
C# Language Dictionaries
C# Language Dictionary with Multiple Keys
C# Language Bit Arrays
C# Language Performance
C# Language Summary
C# Language Memory Management and Pointers
C# Language Memory Management under the Hood
C# Language Freeing Unmanaged Resources
C# Language Unsafe Code
C# Language Summary
C# Language Reflection
C# Language Custom Attributes
C# Language Reflection
C# Language Summary
C# Language Errors and Exceptions
C# Language Looking into Errors and Exception Handling
C# Language Summary
C# Language Visual Studio
C# Language Visual Studio 2009
C# Language Refactoring
C# Language Visual Studio 2009 for .NET Framework 3.0
C# Language Summary
C# Language Deployment
C# Language Designing for Deployment
C# Language Deployment Options
C# Language Deployment Requirements
C# Language Deploying the .NET Runtime
C# Language Simple Deployment
C# Language Installer Projects
C# Language ClickOnce
C# Language Summary
C# Language Base Class Libraries
C# Language Assemblies
C# Language What Are Assemblies?
C# Language Assembly Structure
C# Language Cross-Language Support
C# Language Global Assembly Cache
C# Language Creating Shared Assemblies
C# Language Configuration
C# Language Summary
C# Language Tracing and Events
C# Language Tracing
C# Language Event Logging
C# Language Performance Monitoring
C# Language Summary
C# Language Threading and Synchronization
C# Language Overview
C# Language Asynchronous Delegates
C# Language The Thread Class
C# Language Thread Pools
C# Language Threading Issues
C# Language Synchronization
C# Language COM Apartments
C# Language Background Worker
C# Language Summary
C# Language .NET Security
C# Language Code Access Security
C# Language Support for Security in the Framework
C# Language Managing Security Policies
C# Language Role-Based Security
C# Language Summary
C# Language Localization
C# Language Namespace System.Globalization
C# Language Resources
C# Language Localization Example Using Visual Studio
C# Language Localization with ASP.NET
C# Language A Custom Resource Reader
C# Language Creating Custom Cultures
C# Language Summary
C# Language Transactions
C# Language Overview
C# Language Database and Classes
C# Language Traditional Transactions
C# Language System.Transactions
C# Language Isolation Level
C# Language Custom Resource Managers
C# Language Transactions with Windows Vista
C# Language Summary
C# Language Windows Services
C# Language What Is a Windows Service?
C# Language Windows Services Architecture
C# Language System.ServiceProcess Namespace
C# Language Creating a Windows Service
C# Language Monitoring and Controlling the Service
C# Language Troubleshooting
C# Language Power Events
C# Language Summary
C# Language COM Interoperability
C# Language .NET and COM
C# Language Marshaling
C# Language Using a COM Component from a .NET Client
C# Language Using a .NET Component from a COM Client
C# Language Platform Invoke
C# Language Summary
C# Language Data
C# Language Manipulating Files and the Registry
C# Language Managing the File System
C# Language Moving, Copying, and Deleting Files
C# Language Reading and Writing to Files
C# Language Reading Drive Information
C# Language File Security
C# Language Reading and Writing to the Registry
C# Language Reading and Writing to Isolated Storage
C# Language Summary
C# Language Data Access with .NET
C# Language ADO.NET Overview
C# Language Using Database Connections
C# Language Commands
C# Language Fast Data Access: The Data Reader
C# Language Managing Data and Relationships: The DataSet Class
C# Language Populating a DataSet
C# Language Persisting DataSet Changes
C# Language Working with ADO.NET
C# Language Summary
C# Language Manipulating XML
C# Language XML Standards Support in .NET
C# Language Introducing the System.Xml Namespace
C# Language Using MSXML in .NET
C# Language Using System.Xml Classes
C# Language Reading and Writing Streamed XML
C# Language Using the DOM in .NET
C# Language Using XPathNavigators
C# Language XML and ADO.NET
C# Language Serializing Objects in XML
C# Language Summary
C# Language .NET Programming with SQL Server 2009
C# Language .NET Runtime Host
C# Language Microsoft.SqlServer.Server
C# Language User-Defined Types
C# Language Stored Procedures
C# Language User-Defined Functions
C# Language Triggers
C# Language XML Data Type
C# Language Summary
C# Language Presentation
C# Language Windows Forms
C# Language Creating a Windows Form Application
C# Language Control Class
C# Language Standard Controls and Components
C# Language Forms
C# Language Summary
C# Language Viewing .NET Data
C# Language The DataGridView Control
C# Language DataGridView Class Hierarchy
C# Language Data Binding
C# Language Visual Studio .NET and Data Access
C# Language Summary
C# Language Graphics with GDI+
C# Language Understanding Drawing Principles
C# Language Measuring Coordinates and Areas
C# Language A Note about Debugging
C# Language Drawing Scrollable Windows
C# Language World, Page, and Device Coordinates
C# Language Colors
C# Language The Safety Palette
C# Language Pens and Brushes
C# Language Drawing Shapes and Lines
C# Language Displaying Images
C# Language Issues When Manipulating Images
C# Language Drawing Text
C# Language Simple Text Example
C# Language Fonts and Font Families
C# Language Example: Enumerating Font Families
C# Language Editing a Text Document: The CapsEditor Sample
C# Language Printing
C# Language Summary
C# Language Windows Presentation Foundation
C# Language Overview
C# Language Shapes
C# Language Controls
C# Language Layout
C# Language Event Handling
C# Language Commands
C# Language Styles, Templates, and Resources
C# Language Styles
C# Language Animations
C# Language Data Binding
C# Language Windows Forms Integration
C# Language Summary
C# Language ASP.NET Pages
C# Language ASP.NET Introduction
C# Language ASP.NET Web Forms
C# Language ADO.NET and Data Binding
C# Language Application Configuration
C# Language Summary
C# Language ASP.NET Development
C# Language Custom Controls
C# Language Master Pages
C# Language Site Navigation
C# Language Security
C# Language Themes
C# Language Web Parts
C# Language Summary
C# Language ASP.NET AJAX
C# Language What Is Ajax?
C# Language What Is ASP.NET AJAX?
C# Language ASP.NET AJAX-Enabled Web Sites
C# Language Summary
C# Language Communication
C# Language Accessing the Internet
C# Language The WebClient Class
C# Language WebRequest and WebResponse Classes
C# Language Displaying Output as an HTML Page
C# Language Utility Classes
C# Language Lower-Level Protocols
C# Language Summary
C# Language Web Services with ASP.NET
C# Language SOAP
C# Language WSDL
C# Language Web Services
C# Language Extending the Event-siteing Example
C# Language Exchanging Data Using SOAP Headers
C# Language Summary
C# Language .NET Remoting
C# Language What Is .NET Remoting?
C# Language .NET Remoting Overview
C# Language Contexts
C# Language Remote Objects, Clients, and Servers
C# Language .NET Remoting Architecture
C# Language Miscellaneous .NET Remoting Features
C# Language Summary
C# Language Enterprise Services
C# Language Overview
C# Language Creating a Simple COM+ Application
C# Language Deployment
C# Language Component Services Explorer
C# Language Client Application
C# Language Transactions
C# Language Sample Application
C# Language Integrating WCF and Enterprise Services
C# Language Summary
C# Language Message Queuing
C# Language Overview
C# Language Message Queuing Products
C# Language Message Queuing Architecture
C# Language Message Queuing Administrative Tools
C# Language Programming Message Queuing
C# Language Course Order Application
C# Language Receiving Results
C# Language Transactional Queues
C# Language Message Queue Installation
C# Language Summary
C# Language Windows Communication Foundation
C# Language Overview
C# Language Simple Service and Client
C# Language Contracts
C# Language Service Implementation
C# Language Binding
C# Language Hosting
C# Language Clients
C# Language Duplex Communication
C# Language Summary
C# Language Windows Workflow Foundation
C# Language Activities
C# Language Custom Activities
C# Language Workflows
C# Language The Workflow Runtime
C# Language Workflow Services
C# Language Hosting Workflows
C# Language The Workflow Designer
C# Language Summary
C# Language Download Details
C# Language Directory Services
C# Language The Architecture of Active Directory
C# Language Administration Tools for Active Directory
C# Language Programming Active Directory
C# Language Searching for User Objects
C# Language DSML
C# Language Summary
C# Language Part VII: Additional Information
C# Language C#, Visual Basic, and C++/CLI
C# Language Namespaces
C# Language Defining Types
C# Language Methods
C# Language Static Members
C# Language Arrays
C# Language Control Statements
C# Language Loops
C# Language Exception Handling
C# Language Inheritance
C# Language Resource Management
C# Language Delegates
C# Language Events
C# Language Generics
C# Language C++/CLI Mixing Native and Managed Code
C# Language Summary
C# Language Windows Vista
C# Language Vista Bridge
C# Language User Account Control
C# Language Directory Structure
C# Language New Controls and Dialogs
C# Language Search
C# Language Summary
C# Language Language Integrated Query
C# Language Traditional Queries
C# Language LINQ Query
C# Language Query Expressions
C# Language Extension Methods
C# Language Standard Query Operators
C# Language Lambda Expressions
C# Language Deferred Query Execution
C# Language Expression Trees
C# Language Type Inference
C# Language Object and Collection Initializers
C# Language Anonymous Types
C# Language Summary
C# Language Index
C# Language A
C# Language B
C# Language C
C# Language D
C# Language E
C# Language F
C# Language G
C# Language H
C# Language I
C# Language J
C# Language K
C# Language L
C# Language M
C# Language N
C# Language O
C# Language P
C# Language Q
C# Language R
C# Language S
C# Language T
C# Language U
C# Language V
C# Language W
C# Language X
C# Language Y
C# Language Z
C# Language
C# Language
Previous PagePrevious
Next PageNext

Predefined Data Types

Now that you have seen how to declare variables and constants, this section takes a closer look at the data types available in C#. As you will see, C# is a lot more strict about the types available and their definitions than some other languages are.

Value Types and Reference Types

Before examining the data types in C#, it is important to understand that C# distinguishes between two categories of data type:

  • Value types

  • Reference types

The next few sections look in detail at the syntax for value and reference types. Conceptually, the difference is that a value type stores its value directly, whereas a reference type stores a reference to the value. Compared to other languages, value types in C# are basically the same thing as simple types (integer, float, but not pointers or references) in Visual Basic or C++. Reference types are the same as reference types in Visual Basic or are similar to types accessed through pointers in C++.

These types are stored in different places in memory; value types are stored in an area known as the stack, and reference types are stored in an area known as the managed heap. It is important to be aware of whether a type is a value type or a reference type because of the different effect that assignment has. For example, int is a value type, which means that the following statement will result in two locations in memory storing the value 20:


// i and j are both of type int
i = 20;
j = i;

However, consider the following code. For this code, assume that you have defined a class called Vector. Assume that Vector is a reference type and has an int member variable called Value:


Vector x, y;
x = new Vector();
x.Value = 30;   // Value is a field defined in Vector class
y = x;
Console.WriteLine(y.Value);
y.Value = 50;
Console.WriteLine(x.Value);

The crucial point to understand is that after executing this code, there is only one Vector object around. x and y both point to the memory location that contains this object. Because x and y are variables of a reference type, declaring each variable simply reserves a reference - it doesn’t instantiate an object of the given type. This is the same as declaring a pointer in C++ or an object reference in Visual Basic. In neither case is an object actually created. In order to create an object, you have to use the new keyword, as shown. Because x and y refer to the same object, changes made to x will affect y and vice versa. Hence the code will display 30 then 50.

Tip 

C++ developers should note that this syntax is like a reference, not a pointer. We use the . notation, not ->, to access object members. Syntactically, C# references look more like C++ reference variables. However, behind the superficial syntax, the real similarity is with C++ pointers.

If a variable is a reference, it is possible to indicate that it does not refer to any object by setting its value to null:


y = null;

This is just the same as setting a reference to null in Java, a pointer to NULL in C++, or an object reference in Visual Basic to Nothing. If a reference is set to null, then clearly it is not possible to call any nonstatic member functions or fields against it; doing so would cause an exception to be thrown at runtime.

In languages like C++, the developer could choose whether a given value was to be accessed directly or via a pointer. Visual Basic was more restrictive, taking the view that COM objects were reference types and simple types were always value types. C# is similar to Visual Basic in this regard: whether a variable is a value or reference is determined solely by its data type, so int, for example, is always a value type. It is not possible to declare an int variable as a reference (although in Chapter 6, “Operators and Casts,” which covers boxing, you see it is possible to wrap value types in references of type object).

In C#, basic data types like bool and long are value types. This means that if you declare a bool variable and assign it the value of another bool variable, you will have two separate bool values in memory. Later, if you change the value of the original bool variable, the value of the second bool variable does not change. These types are copied by value.

In contrast, most of the more complex C# data types, including classes that you yourself declare, are reference types. They are allocated upon the heap, have lifetimes that can span multiple function calls, and can be accessed through one or several aliases. The Common Language Runtime (CLR) implements an elaborate algorithm to track which reference variables are still reachable and which have been orphaned. Periodically, the CLR will destroy orphaned objects and return the memory that they once occupied back to the operating system. This is done by the garbage collector.

C# has been designed this way because high performance is best served by keeping primitive types (like int and bool) as value types, while having larger types that contain many fields (as is usually the case with classes) as reference types. If you want to define your own type as a value type, you should declare it as a struct.

CTS Types

As mentioned in Chapter 1, “.NET Architecture,” the basic predefined types recognized by C# are not intrinsic to the language but are part of the .NET Framework. For example, when you declare an int in C#, what you are actually declaring is an instance of a .NET struct, System.Int32. This may sound like an use an easier word point, but it has a profound significance: it means that you are able to treat all the primitive data types syntactically as if they were classes that supported certain methods. For example, to convert an int i to a string, you can write:


string s = i.ToString();

It should be emphasized that, behind this syntactical convenience, the types really are stored as primitive types, so there is absolutely no performance cost associated with the idea that the primitive types are notionally represented by .NET structs.

The following sections review the types that are recognized as built-in types in C#. Each type is listed, along with its definition and the name of the corresponding .NET type (CTS type). C# has 15 predefined types, 13 value types, and 2 (string and object) reference types.

Predefined Value Types

The built-in value types represent primitives, such as integer and floating-point numbers, character, and Boolean types.

Integer types

C# supports eight predefined integer types, shown in the following table.

C# Language Open table as spreadsheet

Name

CTS Type

Description

Range (min:max)

sbyte

System.SByte

8-bit signed integer

-128:127 (-27:27-1)

short

System.Int16

16-bit signed integer

-32,768:32,767 (-215:215-1)

int

System.Int32

32-bit signed integer

-2,147,483,648:2,147,483,647 (-231:231-1)

longt

System.Int64

64-bit signed integer

-9,223,372,036,854,775,808: 9,223,372,036,854,775,807 (-263:263-1)

byte

System.Byte

8-bit unsigned integer

0:255 (0:28-1)

ushort

System.UInt16

16-bit unsigned integer

0:65,535 (0:216-1)

uint

System.UInt32

32-bit unsigned integer

0:4,294,967,295 (0:232-1)

ulong

System.UInt64

64-bit unsigned integer

0:18,446,744,073,709,551,615 (0:264-1)

Future versions of Windows will target 64-bit processors, which can move bits into and out of memory in larger chunks to achieve faster processing times. Consequently, C# supports a rich palette of signed and unsigned integer types ranging in size from 8 to 64 bits.

Many of these type names will be new to Visual Basic. C++ and Java developers should be careful; some of the names of C# types are the same as C++ and Java types, but the types have different definitions. For example, in C#, an int is always a 32-bit signed integer. In C++ an int is a signed integer, but the number of bits is platform-dependent (32 bits on Windows). In C#, all data types have been defined in a platform-independent manner to allow for the possible future porting of C# and .NET to other platforms.

A byte is the standard 8-bit type for values in the range 0 to 255 inclusive. Be aware that, in keeping with its emphasis on type safety, C# regards the byte type and the char type as completely distinct, and any programmatic conversions between the two must be explicitly requested. Also be aware that unlike the other types in the integer family, a byte type is by default unsigned. Its signed version bears the special name sbyte.

With .NET, a short is no longer quite so short; it is now 16 bits long. The int type is 32 bits long. The long type reserves 64 bits for values. All integer-type variables can be assigned values in decimal or in hex notation. The latter require the 0x prefix:


long x = 0x12ab;

If there is any ambiguity about whether an integer is int, uint, long, or ulong, it will default to an int. To specify which of the other integer types the value should take, you can append one of the following characters to the number:


uint ui = 1234U;
long l = 1234L;
ulong ul = 1234UL;

You can also use lowercase u and l, although the latter could be confused with the integer 1 (one).

Floating-Point Types

Although C# provides a plethora of integer data types, it supports floating-point types as well. They will be familiar to C and C++ programmers.

C# Language Open table as spreadsheet

Name

CTS Type

Description

Significant Figures

Range (approximate)

float

System.Single

32-bit single-precision floating point

7

±1.5 × 10−45 to ±3.4 × 1038

double

System.Double

64-bit double-precision floating point

15/16

±5.0 × 10 −324 to ±1.7 × 10308

The float data type is for smaller floating-point values, for which less precision is required. The double data type is bulkier than the float data type but offers twice the precision (15 digits).

If you hard-code in a non-integer number (such as 12.3) in your code, the compiler will normally assume that you want the number interpreted as a double. If you want to specify that the value is a float, you append the character F (or f) to it:


float f = 12.3F;

The decimal Type

In addition, there is a decimal type representing higher-precision floating-point numbers, as shown in the following table.

C# Language Open table as spreadsheet

Name

CTS Type

Description

Significant Figures

Range (approximate)

decimal

System.Decimal

128-bit high precision decimal notation

28

±1.0 × 10−28 to ±7.9 × 1028

One of the great things about the CTS and C# is the provision of a dedicated decimal type for financial calculations. How you use the 28 digits that the decimal type provides is up to you. In other words, you can track smaller dollar amounts with greater accuracy for cents or larger dollar amounts with more rounding in the fractional area. You should bear in mind, however, that decimal is not implemented under the hood as a primitive type, so using decimal will have a performance impact your calculations.

To specify that your number is of a decimal type rather than a double, float, or an integer, you can append the M (or m) character to the value, as shown in the following example:


decimal d = 12.30M;

The Boolean Type

The C# bool type is used to contain Boolean values of either true or false.

C# Language Open table as spreadsheet

Name

CTS Type

Description

Significant Figures

Range (approximate)

decimal

System.Decimal

128-bit high precision decimal notation

28

±1.0 × 10−28 to ±7.9 × 1028

You cannot implicitly convert bool values to and from integer values. If a variable (or a function return type) is declared as a bool, you can only use values of true and false. You will get an error if you try to use zero for false and a non-zero value for true.

The Character Type

For storing the value of a single character, C# supports the char data type.

C# Language Open table as spreadsheet

Name

CTS Type

Values

char

System.Char

Represents a single 16-bit (Unicode) character

Although this data type has a superficial resemblance to the char type provided by C and C++, there is a significant difference. C++ char represents an 8-bit character, whereas a C# char contains 16 bits. This is part of the reason that implicit conversions between the char type and the 8-bit byte type are not permitted.

Although 8 bits may be enough to encode every character in the English language and the digits 0–9, they aren’t enough to encode every character in more expansive symbol systems (such as Chinese). In a gesture toward universality, the computer industry is moving away from the 8-bit character set and toward the 16-bit Unicode scheme, of which the ASCII encoding is a subset.

Literals of type char are signified by being enclosed in single quotation marks, for example ‘A’. If you try to enclose a character in double quotation marks, the compiler will treat this as a string and throw an error.

As well as representing chars as character literals, you can represent them with four-digit hex Unicode values (for example ‘\u0041’), as integer values with a cast (for example, (char)65), or as hexadecimal values (‘\x0041’). They can also be represented by an escape sequence, as shown in the following table.

C# Language Open table as spreadsheet

Escape Sequence

Character

\‘

Single quotation mark

\“

Double quotation mark

\\

Backslash

\0

Null

\a

Alert

\b

Backspace

\f

Form feed

\n

Newline

\r

Carriage return

\t

Tab character

\v

Vertical tab

C++ developers should note that because C# has a native string type, you don’t need to represent strings as arrays of chars.

Predefined Reference Types

C# supports two predefined reference types, described in the following table.

C# Language Open table as spreadsheet

Name

CTS Type

Description

object

System.Object

The root type. All other types in the CTS are derived (including value types) from object.

string

System.String

Unicode character string

The object Type

Many programming languages and class hierarchies provide a root type, from which all other objects in the hierarchy are derived. C# and .NET are no exception. In C#, the object type is the ultimate parent type from which all other intrinsic and user-defined types are derived. This is a key feature of C#, which distinguishes it from both Visual Basic and C++, although its behavior here is very similar to Java. All types implicitly derive ultimately from the System.Object class. This means that you can use the object type for two purposes:

  • You can use an object reference to bind to an object of any particular subtype. For example, in Chapter 6, “Operators and Casts,” you see how you can use the object type to box a value object on the stack to move it to the heap. object references are also useful in reflection, when code must manipulate objects whose specific types are unknown. This is similar to the role played by a void pointer in C++ or by a Variant data type in VB.

  • The object type implements a number of basic, general-purpose methods, which include Equals(), GetHashCode(), GetType(), and ToString(). Responsible user-defined classes may need to provide replacement implementations of some of these methods using an object-oriented technique known as overriding, which is discussed in Chapter 4, “Inheritance.” When you override ToString(), for example, you equip your class with a method for intelligently providing a string representation of itself. If you don’t provide your own implementations for these methods in your classes, the compiler will pick up the implementations in object, which may or may not be correct or sensible in the context of your classes.

The object type is examined in more detail in subsequent chapters.

The string Type

Veterans of C and C++ probably have battle scars from wrestling with C-style strings. A C or C++ string was nothing more than an array of characters, so the client programmer had to do a lot of work just to copy one string to another or to concatenate two strings. In fact, for a generation of C++ programmers, implementing a string class that wrapped up the messy details of these operations was a rite of passage requiring many hours of teeth gnashing and head scratching. Visual Basic programmers had a somewhat easier life, with a string type, while Java people had it even better, with a String class that is in many ways very similar to C# string.

C# recognizes the string keyword, which under the hood is translated to the .NET class, System.String. With it, operations like string concatenation and string copying are a snap:


string str1 = "Hello";
string str2 = "World";
string str3 = str1 + str2; // string concatenation

Despite this style of assignment, string is a reference type. Behind the scenes, a string object is allocated on the heap, not the stack, and when you assign one string variable to another string, you get two references to the same string in memory. However, with string there are some differences from the usual behavior for reference types. For example, should you then make changes to one of these strings, note that this will create an entirely new string object, leaving the other string unchanged. Consider the following code:


using System;

class StringExample
{
   public static int Main()
   {
      string s1 = "a string";
      string s2 = s1;
      Console.WriteLine("s1 is " + s1);
      Console.WriteLine("s2 is " + s2);
      s1 = "another string";
      Console.WriteLine("s1 is now " + s1);
      Console.WriteLine("s2 is now " + s2);
      return 0;
   }
}

The output from this is:

s1 is a string
s2 is a string
s1 is now another string
s2 is now a string

Changing the value of s1 had no effect on s2, contrary to what you’d expect with a reference type! What’s happening here is that when s1 is initialized with the value a string, a new string object is allocated on the heap. When s2 is initialized, the reference points to this same object, so s2 also has the value a string. However, when you now change the value of s1, instead of replacing the original value, a new object will be allocated on the heap for the new value. The s2 variable will still point to the original object, so its value is unchanged. Under the hood, this happens as a result of operator overloading, a topic that is explored in Chapter 6, “Operators and Casts.” In general, the string class has been implemented so that its semantics follow what you would normally intuitively expect for a string.

String literals are enclosed in double quotation marks (“...”); if you attempt to enclose a string in single quotation marks, the compiler will take the value as a char, and throw an error. C# strings can contain the same Unicode and hexadecimal escape sequences as chars. Because these escape sequences start with a backslash, you can’t use this character unescaped in a string. Instead, you need to escape it with two backslashes (\\):


string filepath = "C:\\ProCSharp\\First.cs";

Even if you are confident you can remember to do this all the time, it can prove annoying typing all those double backslashes. Fortunately, C# gives you an alternative. You can prefix a string literal with the at character (@) and all the characters in it will be treated at face value; they won’t be interpreted as escape sequences:


string filepath = @"C:\ProCSharp\First.cs";

This even allows you to include line breaks in your string literals:


string jabberwocky = @"'Twas brillig and the slithy toves
Did gyre and gimble in the wabe.";

Then the value of jabberwocky would be this:

'Twas brillig and the slithy toves
Did gyre and gimble in the wabe.

Previous PagePrevious
Next PageNext