C# Data Types

.C# Data Types
bool | byte | sbyte | char | decimal | double | single | int | uint | long |
ulong | object | short | ushort | string

"The following C# data types (sbyte, ushort, uint, ulong) are not compliant with the Common Language Specification (CLS) and should not be used when writing C# code to interact with non-C# code. More information about Writing CLS-Compliant Code is on MSDN."

C# Data Types

C# 5.0 Alias .NET 4.5 Type Description Size (bits) Range Additional Info Sample Code
bool Boolean Logical Boolean type 8 true or false (bool? also stores null) bool can not be cast to any other type. E.g. "if (1)" is not allowed in C#. Code
byte Byte Unsigned integer 8 0 to 255 In addition to mathematical operations, also supports bitwise AND, OR, XOR, left and right shifts. Code
sbyte SByte Signed integer 8 -128 to 127 Not CLS‑Compliant. unchecked keyword will suppress integral overflow-checking. Code
char Char Represents a character as a UTF-16 code unit 16 C# uses UTF-16 encoding. Some non-English languages require more than one char to make a glyph. Code
decimal Decimal 28-29 significant digits real number for financial calculations 128 79,228,162,514,264,337,593,543,950,335 to -79,228,162,514,264,337,593,543,950,335 For literal to be treated as decimal, use the suffix m or M. Non-native to processor. Base 10. Code
double Double Double-precision floating point type 64 -1.79769313486232e308 to 1.79769313486232e308
(also: negative zero, PositiveInfinity, NegativeInfinity, and not a number (NaN)
Real numeric literals default to double. Use D suffix to treat an integral literal as a double. Code
float Single Single-precision floating point type 32 -3.402823e38 to 3.402823e38
(also: negative zero, PositiveInfinity, NegativeInfinity, and not a number (NaN)
Real number literals default to double, so must use F suffix to indicate a float. Code
int Int32 Signed integer 32 -2,147,483,648 to 2,147,483,647 When an integer literal has no suffix, its type is the first of these types in which its value can be represented: int, uint, long, ulong. Code
uint Uint32 Unsigned integer 32 0 to 4294967295 Not CLS‑Compliant. When you use the suffix U or u, the type of the literal is determined to be either uint or ulong according to the numeric value of the literal. Code
long Int64 Signed integer 64 -9223372036854775808 to 9223372036854775807 When you use the suffix L, the type of the literal integer is determined to be either long or ulong according to its size. Code
ulong Uint64 Unsigned integer 64 0 to 18446744073709551615 Not CLS‑Compliant. Literal suffix of UL designates ulong. Code
object Object Object is the ultimate base class of all classes in the .NET Framework; it is the root of the type hierarchy. All .NET classes inherit these Object methods. (Note: Derived classes can and do override some of these methods).
  1. Equals - Comparisons between objects.
  2. Finalize - Cleanup operations before an object is reclaimed.
  3. GetHashCode - Generates a number corresponding to the value of the object to support the use of a hash table.
  4. ToString - Creates a text string that describes an instance of the class.
Object is a reference type. When a variable of a value type is converted to object, it is said to be "boxed". Code
short Int16 Signed integer 16 -32,768 to 32,767 Short array will take half the space of an int array. However short local variables can actually have worse performance than an int data type. Code
ushort UInt16 Unsigned integer 16 0 to 65,535 Not CLS‑Compliant. Code
string String A sequence of zero or more Unicode characters. Verbatim string literals start with @ and do not require escape sequences (e.g. @"c:\Docs\Financial\contract.txt";) See the StringBuilder vs StringConcatenation video demonstration for concatenation performance comparisons and the Object and String Comparisons video for information about String Intern Pools. Although string is a reference type, the equality operators (== and !=) are defined to compare the values of string objects, not references. Code


Explicit Data Type Conversion (casting required)

From To
byte sbyte or char
sbyte byte, ushort, uint, ulong, or char
char sbyte, byte, or short
decimal sbyte, byte, short, ushort, int, uint, long, ulong, char, float, or double
double sbyte, byte, short, ushort, int, uint, long, ulong, char, float, or decimal
float sbyte, byte, short, ushort, int, uint, long, ulong, char, or decimal
int sbyte, byte, short, ushort, uint, ulong, or char
uint sbyte, byte, short, ushort, int, or char
long sbyte, byte, short, ushort, int, uint, ulong, or char
ulong sbyte, byte, short, ushort, int, uint, long, or char
short sbyte, byte, ushort, uint, ulong, or char
ushort sbyte, byte, short, or char


C# Data Type Categories

Value Types = built-in type, struct, enum

ReferenceTypes = class, string, array, interface, delegate

C# is a strongly-typed language because its type rules are strictly enforced. C# enforces type safety at compile time and at run time. Before a value can be stored in a variable, the type of the variable must be known. Once a variable's type is set, its type can not be changed. C# 3.0 introduced the var keyword to implicitly type variables declared at method scope. An implicitly typed variable is still strongly typed, but the compiler determines the type to use. Implicitly typed variables are required for some LINQ queries where the result is a collection of anonymous types. (Note: An exception to the static typing rule was introduced in C# 4.0 with the dynamic keyword which can be used to mark code to be skipped during the compile-time type checking.)

All C# types belong to one of the following four categories:

  1. Value Type
    • The contents of a value type is simply a value.
    • A value type stores its contents in memory allocated on the stack.
    • When the variable goes out of scope the value is discarded from the stack.
    • Using the stack is efficient, but the limited lifetime of value types makes them less suited for sharing data between different classes.
    • Value types are usually short-lived, but the stack has a limited allocation size. The rule of thumb is to keep value types less than 16 bytes.
    • Value types require copy operations, which require more overhead that just switching a pointer value.
    • A value type is for variables which are declared using one of the built-in data types, and for an enum or a structure. An exception to the built-in rule is the string data type, which is a reference type.
    • struct definitions create value types. You can not inherit from a structure (implicitly sealed). Again, should be less than 16 bytes. Example usage of a structure would be a point (x,y) or (x,y,z) integers.
    • Value types do not support null values. However the Nullable<T> structure can be used to allow the addition of null values to value types. (i.e. bool=(true, false); bool? = (true, false, null)).
    • By default, parameters in C# are passed by value.
  2. Reference Types
    • Reference types contain two parts: an object and a reference to the object.
    • A reference type, such as an instance of a class or an array, is allocated in a different area of memory called the heap.
    • The memory used by reference type isn't returned to the heap when a method finishes; it's only reclaimed when C#'s garbage collection system determines it is no longer needed.
    • There is a greater overhead in declaring reference types, but they have the advantage of being accessible from other classes.
    • Two reference types are equal if they point to the same memory location.
    • Reference types can be assigned null values.
    • When passing parameters by reference, you must use one of these two keywords (use the keyword in both the parameter list and the method call):
      1. ref - passes a value into a method and returns the value from the method. An input-output parameter. These must be initialized before used in the parameter list.
      2. out - returns a value from the method. An output parameter.
  3. Pointer Types
    • C# has mostly eliminated the need for memory pointers. However C# does support pointers for direct memory manipulation within blocks of code marked unsafe and compiled with the /unsafe compiler option. Unsafe code can only be executed in fully trusted assemblies.
    • Unsafe code is not necessarily dangerous. It is just code whose safety can not be verified by the CLR.
    • Pointers may be needed for performance-critical areas or for interoperability with C APIs.
    • The unsafe keyword can be used as a modifier to a method, property, etc. or to mark a block of statements.
    • Unsafe code is outside the control of garbage collection.
    • Pointers can point to types which are outside the control of garbage collection, such as value types and arrays (a.k.a. "unmanaged variables").
    • Pointers are of little use for types under the control of garbage collection ("managed variables") because garbage collection can relocate the variables unpredictably. So pointers to managed variables require the fixed statement to "pin" the variable (object) to one memory location. The C# compiler only lets you assign a pointer to a managed variable in a fixed statement.
    • Pointer operators include:
      • & - address-of operator, returns a pointer to the address of a value
      • *- dereference operator, returns the value at the address of a pointer
      • ->pointer-to-member operator, a syntactic shortcut where x -> y is the same as (*x).y
    • Example unsafe code with pointer:

      namespace UnsafeApp
      {
          class Program
          {
              static unsafe void Main(string[] args)
              {
                  int myInt = 30;
                  int* ip = &myInt; // ip is an integer pointer containing the address of myInt

                  Console.WriteLine("Data is: {0} ", myInt);     //Prints: 30
                  Console.WriteLine("Data is: {0} ", *ip);       //Prints: 30 (dereference ip)
                  Console.WriteLine("Address is: {0}", (int)ip); //Prints: address in ip
                  myInt = 35;
                  Console.WriteLine("Data is: {0} ", *ip);       //Prints: 35
                  Console.ReadKey();
              }
          }
      }

  4. Generic Type Parameters
    • Generics were introduced in C# 2.0 and allow for code reuse without incurring the cost or risk of runtime casts or boxing operations. Generics allow for the creation of classes, structures, interfaces and methods that have placeholders for one or more of the types they use.
    • A generic type parameter is a placeholder for a type which makes it possible for generic code to defer the specification of one or more types until the code is declared and instantiated. For example, in the generic method: public static void GenericWriteLine(T myGenericParm) (see example below), the type parameter "T" must be replaced by a type recognized by the compiler and within the generic data type constraints (if constraints were specified).
    • If only one type is used in the generic code, it is common to use the single uppercase letter "T" as the generic type parameter. If more than one type is used in the generic code, it is common to prefix each generic type parameter with the uppercase letter "T".
    • Example of static generic method:

      namespace GenericsExample
      {
          class Program
          {
              // Generic Method
              public static void GenericWriteLine<T>(T myGenericParm)
              {
                  Console.WriteLine(myGenericParm.ToString());
                  Console.WriteLine(myGenericParm.GetType());
              }

              static void Main(string[] args)
              {
                  int x = 5;                   // Prints: 5
                  GenericWriteLine<int>(x);    // Prints: Int32

                  double y = 6.5;              // Prints: 6.5
                  GenericWriteLine<double>(y); // Prints: Double

                  string z = "Hello Generics"; // Prints: Hello Generics
                  GenericWriteLine<string>(z); // Prints: String
              }
          }
      }


Note: By Default, parameters are passed to methods by value. When passed "by value", changes to the passed value are not propogated back to the caller. When parameters are passed to methods by reference (using ref or out keywords), then changes to the passed value are propogated back to the caller.

The stack is the memory set aside as scratch space for a thread of execution. When a function is called, a block is reserved on the top of the stack for local variables and some bookkeeping data. When that function returns, the block becomes unused and can be used the next time a function is called. The stack is always reserved in a LIFO order; the most recently reserved block is always the next block to be freed. This makes it really simple to keep track of the stack; freeing a block from the stack is nothing more than adjusting one pointer.

The heap is memory set aside for dynamic allocation. Unlike the stack, there's no enforced pattern to the allocation and deallocation of blocks from the heap; you can allocate a block at any time and free it at any time. This makes it much more complex to keep track of which parts of the heap are allocated or free at any given time; there are many custom heap allocators available to tune heap performance for different usage patterns.

Each thread gets a stack, while there's typically only one heap for the application (although it isn't uncommon to have multiple heaps for different types of allocation).

C# Constants

Constants are immutable values which are known at compile time and do not change for the life of the program. Constants are declared with the const modifier. Only C# primitive data types (excluding System.Object) may be declared as const. User-defined types, including classes, structs, and arrays, cannot be const. Use the readonly modifier to create a class, struct, or array that is initialized one time at runtime (for example in a constructor) and thereafter cannot be changed. C# does not support const methods, properties, or events.

Constant Example - Definition

const int months = 12, weeks = 52, days = 365;



C# Enumerations

Enumerations, or enums, are used to create a group named constants similar to how they are used in C and C++. In C#, enums are value types, and the underlying data type is "int" by default. Also by default, the first enumerator has the value 0, and the value of each successive enumerator is increased by 1.

Enum Example 1 - a simple Color enumeration

public enum Color
{
    Green,    //defaults to 0
    Orange,  //defaults to 1
    Red,       //defaults to 2
    Blue       //defaults to 3
}  



Enum Example 2 - Enumerators can use initializers to override the default values

public enum Color2
{
    Green = 10,
    Orange = 20,
    Red = 30,
    Blue = 40
}



Every enumeration type has an underlying type, which can be any integral type except char. The default underlying type of enumeration elements is int. The approved types for an enum are byte, sbyte, short, ushort, int, uint, long, or ulong. To declare an enum of another integral type, such as byte, use a colon after the identifier followed by the type.

Enum Example 3 - declare an enum of another integral type ( byte)

enum Days : byte {Sat=1, Sun, Mon, Tue, Wed, Thu, Fri};



Below is example code which uses enumeration:

.Enumeration Example


C# Strings

Strings are reference types. A string is an object of type String whose value is text. Internally, the text is stored as a sequential read-only collection of Char objects. There is no null-terminating character at the end of a C# string; therefore a C# string can contain any number of embedded null characters ('\0'). The Length property of a string represents the number of Char objects it contains, not the number of Unicode characters.

String objects are immutable: they cannot be changed after they have been created. All of the String methods and C# operators that appear to modify a string actually return the results in a new string object. Because a string "modification" is actually a new string creation, you must use caution when you create references to strings. The following example illustrates that string modification is actually creating new strings.

.String Modification Actually Creates New Strings


Data Type Conversion

C# is statically-typed at compile time, after a variable is declared, it cannot be declared again or used to store values of another type unless that type is convertible to the variable's type. Converting between data types can be done implicitly, in which the conversion is done automatically by the compiler, or explicitly using a cast, in which the programmer forces the conversion, and assumes the risk of losing information.

Implicit vs Explicit Data Type Conversions

int i = 0;
double d = 0;

i = 10;
d = i;        // An implicit conversion

d = 3.5;
i = (int) d;  // An explicit conversion, or "cast"


Parameter Data Types

The data type of parameters have different behaviors.

A variable of a reference type does not contain its data directly; it contains a reference to its data. When you pass a reference-type parameter by value, it is possible to change the data pointed to by the reference, such as the value of a class member. However, you cannot change the value of the reference itself; that is, you cannot use the same reference to allocate memory for a new class and have it persist outside the block. To do that, pass the parameter using the ref or out keyword.

A value-type variable contains its data directly as opposed to a reference-type variable, which contains a reference to its data. Passing a value-type variable to a method by value means passing a copy of the variable to the method. Any changes to the parameter that take place inside the method have no affect on the original data stored in the argument variable. If you want the called method to change the value of the parameter, you must pass it by reference, using the ref or out keyword. For simplicity, the following examples use ref.

The following example shows the difference between value and reference parameters.


.Value vs Reference Parameters


Boxing and Unboxing

Boxing is process of converting a value type to a reference type. When you box a variable, you are creating a reference variable that points to a new copy on the heap. The reference variable is an object, and therefore can use all the methods that every object inherits, such as, ToString(), Equals(), GetType(), and GetHashCode(). Note: If you're working with older APIs before generics, you are more likely to encounter boxing.

// Boxing Example
int i = 67;             // i is a value type  (stored on stack)
object o = i;           // i is boxed into an object (stored in heap)
Console.WriteLine(o);   // Prints: 67
Console.WriteLine(o.GetType()); // Prints: Int32

// UnBoxing Example
int k = (int)o;         // k is a value type  (unboxed, stored on stack)
Console.WriteLine(k);   // Prints: 67
Console.WriteLine(k.GetType()); // Prints: Int32



bool

bool x = false;
Boolean y = true;
bool? z = null;
if (x) Console.WriteLine("x is true");
if (y) Console.WriteLine("x is true");
if (z == null) Console.WriteLine("x is null");
// if (1) Console.WriteLine("This is not allowed in C#");

Top


byte

byte x = Byte.MinValue;
byte y = 255;
byte z = Byte.Parse("23");

Console.WriteLine("x is: {0}", x); // Prints: x is: 0
Console.WriteLine("y is: {0}", y); // Prints: y is: 255
Console.WriteLine("z is: {0}", z); // Prints: z is: 23

// 3 digits, suppress leading zeros (default formatting).
Console.WriteLine("z is: {0,-3}", z.ToString()); // Prints: z is: 23

// 3 digits and leading zeros.
Console.WriteLine("z is: {0}", z.ToString("D3")); // Prints: z is: 023

// 4 hexadecimal digits.
Console.WriteLine("z is: {0}", z.ToString("X4")); // Prints: z is: 0017

// binary.
Console.WriteLine("z is: {0}", Convert.ToString(z,2)); // Prints: z is: 10111

// bitwise and.
Console.WriteLine("z is: {0}", z & 0xF0); // Prints: z is: 16

// Narrowing conversion required explicit cast
//x = z + z; // Error: conversion from int to byte
x = (byte)(z + z); // OK: explicit cast
Console.WriteLine("x is: {0}", x); // Prints: x is: 46

Top


sbyte

sbyte x = SByte.MinValue;
sbyte y = 127;
sbyte z = SByte.Parse("-23");

Console.WriteLine("x is: {0}", x); // Prints: x is: -128
Console.WriteLine("y is: {0}", y); // Prints: y is: 127
Console.WriteLine("z is: {0}", z); // Prints: z is: -23

// 3 digits, suppress leading zeros (default formatting).
Console.WriteLine("z is: {0,-3}", z.ToString()); // Prints: z is: -23

// size in bytes
Console.WriteLine("sizeof:  {0}", sizeof(sbyte)); // Prints: sizeof: 1

// default value
Console.WriteLine("default: {0}", default(sbyte)); // Prints: default: 0

// .NET type
Console.WriteLine("type:    {0}", x.GetType()); // Prints: type: System.SByte

// C# alias
Console.WriteLine("code:    {0}", x.GetTypeCode()); // Prints code: SByte

Top


char

Note: "U+I" is displayed to represent the ninth letter of the Gothic alphabet (U+10338). This was done because the collation on the underlying Drupal database (MySQL 5.1.30) is set to utf8_general_ci and is not able to store the supplemental Gothic grapheme.

char x = 'k';
char z = '\u006b'// 1 grapheme is 2 bytes here

//char thiuth = 'U+I';  //Error: Too many chars in char literal
string thiuth = "U+I"// Ninth letter of Gothic alphabet U+10338
                        // 1 grapheme is 4 bytes here
                        // UTF-16 (hex) 0xD800 0xDF38 (d800df38)
                        // UTF-8 (hex) 0xF0 0x90 0x8C 0xB8 (f0908cb8)

Console.WriteLine(x.CompareTo('h'));                // Prints:  3 ('h' is 3 before 'k')
Console.WriteLine(x.CompareTo('m'));                // Prints: -2 ('m' is 2 after 'k')

Console.WriteLine(z.Equals('k'));                        // Prints: True (k is encoded as U+006B)
Console.WriteLine(z.Equals((char)107));                // Prints: True (k is encoded as 107 decimal)


Console.WriteLine(Char.IsSurrogate(z));        // Prints: False (k is not part of a surrogate pair)
Console.WriteLine('\u006b'.ToString().Length); // Prints: 1 (String.Length counts UTF-16 chars)

Console.WriteLine("U+I".Length); // Prints: 2 (Supplementary char U+I is outside BMP so is two UTF-16 chars)
Console.WriteLine(Char.IsSurrogate(thiuth[0]));      // Prints: True (is part of a surrogate pair)
Console.WriteLine(Char.IsSurrogate(thiuth[1]));      // Prints: True (is part of a surrogate pair)
Console.WriteLine(Char.IsHighSurrogate(thiuth[0]));  // Prints: True (0xD800 is high-order surrogate pair (2 16-bit code points))
                                                       // High order surrogate from U+D800 thru U+DBFF
Console.WriteLine(Char.IsLowSurrogate(thiuth[1])); // Prints: True (0xDF38 is low-order surrogate pair (2 16-bit code points))
                                                       // Low order surrogate from U+DC00 thru U+DFFF

// Byte Ordering  
String myChar = "U+I";
byte[] thiuthBytes = Encoding.Unicode.GetBytes(myChar); // 4 bytes (32 bits)
           
Console.WriteLine(BitConverter.IsLittleEndian);        // Prints: True (PC=Little-Endian; MF,RISC=Big-Endian)
foreach (byte aByte in thiuthBytes)
    Console.Write("{0:X2}", aByte);
Console.WriteLine();                                // Prints: 00D838DF  (for 0xD800 0xDF38) 

Top


decimal

            decimal x = 0.999m;
            decimal y = 9999999999999999999999999999m;
            //Prints: My amount = $1.00
            Console.WriteLine("My amount = {0:C}", x);  
            //Prints:Your amount = $9,999,999,999,999,999,999,999,999,999.00
            Console.WriteLine("Your amount = {0:C}", y);

Top


double

double x = 6;   // Integer literal implicitly cast to a double
double y = 6D;  // Double literal
double z;

// Integer Quotient from Int Division
z =  1 / 6;
Console.WriteLine("{0:G}", z);  // Prints: 0, because 1 and 6 are type int

// Floating Point Quotient from Floating Point Division
z = 1 / 6.0;
Console.WriteLine("{0:G}", z);  // Prints: 0.166666666666667

// Floating Point Quotient (Round-Trip) from  Floating Point Division
z = 1 / 6.0;
Console.WriteLine("{0:R}", z);  // Prints: 0.16666666666666666

// Floating Point Quotient (Rounded) from Floating Point Division
z = 1 / 6D;
Console.WriteLine("{0:F2}", z);  // Prints: 0.17

Top


float

            // Compiler Error: can't implicitly convert double to float
            //float x = 3.5;

            // If no double in real number expression, type is float (Single).
            float x = 3.5F;
            int y = 2;
            short z = 4;
            var result = x * y / z;
            Console.WriteLine("{0}", result.GetType());  // Prints: Single

            // Infinities and Not a Number (NaN)
            Console.WriteLine(1.0F / 0.0F);  // Prints: Infinity
            Console.WriteLine(-1.0F / 0.0F); // Prints: -Infinity
            Console.WriteLine(1.0F / -0.0F);  // Prints: -Infinity
            Console.WriteLine(-1.0F / -0.0F);  // Prints: Infinity
            Console.WriteLine(0.0F / 0.0F);  // Prints: NaN
            Console.WriteLine(0.0F / 0.0F == float.NaN); // Prints: False
            Console.WriteLine(float.IsNaN(0.0F / 0.0F)); // Prints: True
           
            // Negative Zero and Positive Zero compare as equal
            float a = -0.0F;
            float b = 0.0F;
            Console.WriteLine((a == b ? "Equal" : "Not Equal")); // Prints: Equal

            //Print Hex for Positive and Negative Zeros
            byte[] ah = BitConverter.GetBytes(a);
            string myString = null;
            foreach (byte by in ah)
            {
                myString += by.ToString("X2");
            }
            Console.WriteLine(myString);  // Negative Zero = 0X00000080

            byte[] bh = BitConverter.GetBytes(b);
            myString = null;
            foreach (byte by in bh)
            {
                myString += by.ToString("X2");
            }
            Console.WriteLine(myString);  // Positive Zero = 0X00000000

Top


int

// Convert String to Integer with TryParse
string s1 = "2468";
int myInt = 0;

if (Int32.TryParse(s1, out myInt))
{
    Console.WriteLine("Successfull convert to integer: {0}", myInt);
}

// Int32 implements the IConvertible Interface (Convert)
decimal d = 123.456M;
Console.WriteLine(Convert.ToInt32(d)); // Prints: 123

// Integral overflow wraps from min value to max value
int x = Int32.MinValue;
Console.WriteLine(x); // Prints: -2147483648 (MinValue)
x--;
Console.WriteLine(x); // Prints: 2147483647 (MaxValue)

// Overflow Compiler Error
//int y = Int32.MinValue - 1;

// Unchecked Integral Overflow - compiles okay
int y = unchecked(Int32.MinValue - 1);
Console.WriteLine(y);  // Prints: 2147483647

Top


uint

uint myUint = 4294967295;
Console.WriteLine(uint.MaxValue); // Prints: 4294967295

Console.WriteLine(44U.GetType()); // Prints: UInt32 (uint)
Console.WriteLine(323442434344U.GetType()); // Prints: UInt64 (ulong)

Top


long

long long2 = 9223372036854775807L;
Console.WriteLine(long.MaxValue); // Prints: 9223372036854775807

Console.WriteLine(9223372036854775807L.GetType()); // Prints: Int64 (long)
Console.WriteLine(9223372036854775808L.GetType()); // Prints: UInt64 (ulong)

Top


ulong

// Compiler Error: Can not implicitly convert double to ulong
//ulong x = 4.0;

ulong ulong1 = 8UL;
ulong ulong2 = 18446744073709551615;
Console.WriteLine(ulong.MaxValue); // Prints: 18446744073709551615

Top


object

            object a;
            a = 1;        // an example of boxing (implicit)
           
            Console.WriteLine(a);           // Prints: 1
            Console.WriteLine(a.GetType()); // Prints: Int32
            Console.WriteLine(a.ToString()); // Prints: 1

            decimal k = 12.99M;
            object b = k; // another example of boxing
            Console.WriteLine(b);           // Prints: 12.99
            Console.WriteLine(b.GetType()); // Prints: Decimal
            k = 789.88M;                    // Changing k does not affect b
            Console.WriteLine(b);           // Prints: 12.99

            decimal m = (decimal)b;  // unboxing (explicit)
            Console.WriteLine(m.GetType()); // Prints: Decimal
            Console.WriteLine(m.ToString()); // Prints: 12.99

            Cost price = new Cost(); // Custom Type
            Console.WriteLine(price.GetType()); // Prints: Cost
            Console.WriteLine(price.ToString()); // Prints: Cost 
            Console.WriteLine(price.c.ToString()); // Prints: 9.95

        }

        class Cost
        {
            public decimal c = 9.95M;
        }

Top


short

short x = 5, y = 12;

// Compiler Error: can not implicitly convert int to short
//short z = x + y;

short z = (short)(x + y); // OK: explicit conversion

Top


ushort

ushort x = 65535;
x++;
Console.WriteLine(x);  // Prints: 0 (No compiler or runtime error)

Top


string

            Console.WriteLine("StringBuilder running ...");
            StringBuilder sb1 = new StringBuilder(null);
            for (int i = 0; i < 2E7; i++)
            {
                sb1.Append('X');
            }
            string s2 = sb1.ToString();
            Console.WriteLine("Length of s2 is: {0}", s2.Length.ToString("E"));

            // String Intern Pooling
            Console.WriteLine("I ------------------");  
            string s3 = "kevin";
            string s4 = "kevin harris";
            string s5;

            Console.WriteLine("Result is: {0}", ReferenceEquals(s3, s4));   // False
            s3 += " harris";
            Console.WriteLine("Result is: {0}", s3.Equals(s4));             // True;
            Console.WriteLine("Result is: {0}", ReferenceEquals(s3, s4));   // False ! (Same Value, But Not Interned)
           
            s5 = String.Intern(s3);
            Console.WriteLine("Result is: {0}", ReferenceEquals(s5, s3));   // False !
            Console.WriteLine("Result is: {0}", (s5 == s3));                // True
            Console.WriteLine("Result is: {0}", s5.Equals(s3));             // True
            Console.WriteLine("Result is: {0}", ReferenceEquals(s5, s4));   // True !
           

Top



Reference Articles

Top