Pages

Wednesday, 22 July 2015

is And as Operator

The is Operator

Some conversion attempts are not successful and raise an InvalidCastException exception at run time. Instead of blindly attempting a conversion, you can use the is operator to check whether a conversion would complete successfully.
The syntax of the is operator is the following:
[Class Object] is [Target Type]

The operator returns true if class object can be successfully converted to the target type through any a reference, boxing, and unboxing conversion.

For example:
class Person
{
 public string Name = "Anonymous";
 public int Age = 25;
}
class Employee : Person { }

class Program
{
 static void Main()
 {
  Employee bill = new Employee();
  Person p;
  // Check if variable bill can be converted to type Person
  if( bill is Person )
  {
   p = bill;
   Console.WriteLine("Person Info: " + p.Name + ", " + p.Age);
  }
 }
}

The is operator can be used only for reference conversions and boxing and unboxing conversions. It cannot be used for user-defined conversions.

The as Operator

The as operator is like the cast operator, except that it does not raise an exception. If the conversion fails, rather than raising an exception, it returns null.
The syntax of the as operator is the following:
[Class Object] as [Target Type]

Since the as operator returns a reference expression, it can be used as the source for an assignment, for example:
class Person
{
 public string Name = "Anonymous";
 public int Age = 25;
}
class Employee : Person { }

class Program
{
 static void Main()
 {
  Employee bill = new Employee();
  Person p;
  p = bill as Person;
  if( p != null )
  {
   Console.WriteLine("Person Info: " + p.Name + ", " + p.Age);
  }
 }
}

Like the is operator, the as operator can be used only for reference conversions and boxing conversions. It cannot be used for user-defined conversions or conversions to a value type.

User-Defined Conversions

User-Defined Conversions

Besides the standard conversions, you can also define both implicit and explicit conversions for your own classes and structs.
The syntax for user-defined conversions is shown in the following code.
public static implicit operator [Target Type] ( [Source Type] [Identifier] )
{
 ...
 return [Object Of Target Type];
}
For example, the following shows an example of the syntax of a conversion method that converts an object of type Person to an int:
public static implicit operator int(Person p)
{
 return p.Age;
}

Constraints on User-Defined Conversions

There are some important constraints on user-defined conversions. The most important are the following:
  • You can only define user-defined conversions for classes and structs.
  • You cannot redefine standard implicit or explicit conversions.
  • The following are true for source type S and target type T:
    • S and T must be different types.
    • S and T cannot be related by inheritance. That is, S cannot be derived from T, and T cannot be derived from S.
    • Neither S nor T can be an interface type or the type object.
    • The conversion operator must be a member of either S or T.
  • You cannot declare two conversions, one implicit and the other explicit, with the same source and target types.

Here's the sample of how to make user-defined conversions:
class Person
{
 public string Name;
 public int Age;
 
 public Person(string name, int age)
 {
  Name = name;
  Age = age;
 }
 
 public static implicit operator int(Person p) // Convert Person to int.
 {
  return p.Age;
 }
 
 public static implicit operator Person(int i) // Convert int to Person.
 {
  return new Person("Nemo", i); 
 }
}

class Program
{
 static void Main( )
 {
  Person bill = new Person( "bill", 25);
  int age = bill;
  Console.WriteLine("Person Info: " + bill.Name + ", " + age);
  
  Person anon = 35;
  Console.WriteLine("Person Info: " + anon.Name + ", " + anon.Age);
 }
}

The code will display following output:
Person Info: bill, 25
Person Info: Nemo, 35

If you had defined the same conversion operators as explicit rather than implicit, then you would have needed to use cast expressions to perform the conversions, as shown here:
...
 public static explicit operator int( Person p )
 {
  return p.Age;
 }
...
static void Main( )
{
 ... 
 int age = (int) bill;  //Requires cast expression
 ...
}

Boxing Conversions

Boxing Conversions

All C# types, including the value types, are derived from type object. Value types, however, are efficient, lightweight types that do not, by default, include their object component in the heap. When the object component is needed, however, you can use boxing, which is an implicit conversion that takes a value type value, creates from it a full reference type object in the heap, and returns a reference to the object. For example
int i = 12;
object oi = null;
oi = i;
line 1 and 2: declare and initialize value type variable i and reference type variable oi.
line 3: assign the value of variable i to oi. But oi is a reference type variable and must be assigned a reference to an object in the heap and variable i is a value type and doesn’t have a reference to an object in the heap.
The system therefore boxes the value of i by doing the following:
  • Creating an object of type int in the heap
  • Copying the value of i to the int object
  • Returning the reference of the int object to oi to store as its reference


Boxing Creates a Copy

A common misconception about boxing is that it acts upon the item being boxed. It doesn’t. It returns a reference type copy of the value. After the boxing procedure, there are two copies of the value—the value type original and the reference type copy—each of which can be manipulated separately.

Lets see following code sample:
 int i = 10;  // Create and initialize value type
 object oi = i;  // Create and initialize reference type
 Console.WriteLine("i: " + i + ", io: " + oi);
 i = 12;
 oi = 15;
 Console.WriteLine("i: " + i + ", io: " + oi);

This code produces the following output:
i: 10, io: 10
i: 12, io: 15

The Boxing Conversions

Following figure shows the boxing conversions. Any value type ValueTypeS can be implicitly converted to any of types object, System.ValueType, or InterfaceT, if ValueTypeS implements InterfaceT.

Unboxing Conversions

Unboxing is the process of converting a boxed object back to its value type.
  • Unboxing is an explicit conversion.
  • The system performs the following steps when unboxing a value to ValueTypeT:
    • It checks that the object being unboxed is actually a boxed value of type ValueTypeT.
    • It copies the value of the object to the variable.
static void Main()
{
 int i = 10;
 object oi = i;

 int j = (int) oi;
 Console.WriteLine("i: " + i + ", oi: " + oi + ", j: " + j);
}
This code produces the following output:
i: 10, oi: 10, j: 10

Attempting to unbox a value to a type other than the original type raises an InvalidCastException exception.

Reference Conversions

Reference Conversions

As you well know by now, reference type objects comprise two parts in memory: the reference and the data.
  • Part of the information held by the reference is the type of the data it is pointing at.
  • A reference conversion takes a source reference and returns a reference pointing at the same place in the heap but “labels” the reference as a different type.
For example, the following code shows two reference variables, myVar1 and myVar2, that point to the same object in memory.
class A { public int Field1; }
class B: A { public int Field2; }

class Program
{
 static void Main( )
 {
  B myVar1 = new B();
  A myVar2 = (A) myVar1;
  Console.WriteLine(myVar2.Field1); // Fine
  Console.WriteLine(myVar2.Field2); // Compile error!
 }
} 


Implicit Reference Conversions

Just as there are implicit numeric conversions that the language will automatically perform for you, there are also implicit reference conversions.
  • All reference types have an implicit conversion to type object.
  • Any interface can be implicitly converted to an interface from which it is derived.
  • A class can be implicitly converted to any class in the chain from which it is derived or any interface that it implements.

A delegate can be implicitly converted to the .NET BCL classes and interfaces shown in following figure.

An array, ArrayS, with elements of type Ts, can be implicitly converted to the following:
  • The .NET BCL class and interfaces.
  • Another array, ArrayT, with elements of type Tt, if all of the following are true:
    • Both arrays have the same number of dimensions.
    • The element types, Ts and Tt, are reference types—not value types.
    • There is an implicit conversion between types Ts and Tt.

Explicit Reference Conversions

Explicit reference conversions are reference conversions from a general type to a more specialized type.
Explicit conversions include conversions from an object to any reference type and conversions from a base class to a class derived from it.

This type of conversion were allowed without restriction, you could easily attempt to reference members of a class that are not actually in memory. The compiler, however, does allow these types of conversions. But when the system encounters them at run time, it raises an exception.
For example:
class A { public int Field1; }
class B: A { public int Field2; }

class Program
{
 static void Main( )
 {
  B myVar1 = new B();
  A myVar2 = (A) myVar1;  //unsafe - could raise an exception
 }
} 


Valid Explicit Reference Conversions

There are three situations in which an explicit reference conversion will succeed at run time—that is, not raise an InvalidCastException exception.

The first case is where the explicit conversion is unnecessary—that is, where the language would have performed an implicit conversion for you anyway. For example, in the following code, the explicit conversion is unnecessary because there is always an implicit conversion from a derived class to one of its base classes.
class A { }
class B: A { }
...
 B myVar1 = new B();
 A myVar2 = (A) myVar1; // Cast is unnecessary; A is the base class of B.

The second case is where the source reference is null. For example, in the following code, eventhough it would normally be unsafe to convert a reference of a base class to that of a derived class, the conversion is allowed because the value of the source reference is null.
class A { }
class B: A { }
...
 A myVar1 = null;
 B myVar2 = (B) myVar1; // Allowed because myVar1 is null

The third case is where the actual data pointed to by the source reference could safely be converted implicitly. The following code shows an example.
class A { }
class B: A { }
...
 B myVar1 = new B();
 A myVar2 = myVar1; // Implicitly cast myVar1 to type A.
 B myVar3 = (B)myVar2; // This cast is fine because the data is of type B.

Types of Conversions

Types of Conversions

There are a number of standard, predefined conversions for the numeric and reference types, as following figure:

Numeric Conversions

Any numeric type can be converted into any other numeric type, as illustrated in following figure. Some of the conversions are implicit conversions, and others must be explicit.

Implicit Numeric Conversions

The figure demonstrates that, as you would expect, there is an implicit conversion between numeric types that occupy fewer bits to those that occupy more bits.
  • There is an implicit conversion from the source type to the target type if there is a path, following the arrows, from the source type to the target type.
  • Any numeric conversion for which there is not a path following the arrows from the source type to the target type must be an explicit conversion.

Explicit Numeric Conversions

With the explicit conversions, however, there is the possibility of losing data—so it’s important for you as the programmer to know how a conversion will handle that loss if it occurs.

Integer Type to Integer Type

Following figure shows the behavior of the integer-to-integer explicit conversions. In the checked case, if the conversion loses data, the operation raises an OverflowException exception. In the unchecked case, any lost bits go unreported.

float or double to Integer Type

When converting a floating-point type to an integer type, the value is rounded toward 0 to the nearest integer. Following figure illustrates the conversion conditions. If the rounded value is not within the range of the target type, then
  • The CLR raises an OverflowException exception if the overflow checking context is checked.
  • C# does not define what its value should be if the context is unchecked.

decimal to Integer Type

When converting from decimal to the integer types, the CLR raises an OverflowException exception if the resulting value is not within the target type’s range.

double to float

Values of type float occupy 32 bits, and values of type double occupy 64 bits. When a double is rounded to a float, the double type value is rounded to the nearest float type value.
  • If the value is too small to be represented by a float, the value is set to either positive or negative 0.
  • If the value is too large to be represented by a float, the value is set to either positive or negative infinity.

float or double to decimal

Following action will occur when converting float or double to decimal:
  • If the value is too small to be represented by the decimal type, the result is set to 0.
  • If the value is too large, the CLR raises an OverflowException exception.

decimal to float or double

Conversions from decimal to the floating-point types always succeed. There might, however, be a loss of precision.

Implicit Conversions

Implicit Conversions

For certain types of conversions, there is no possibility of loss of data or precision. For example, it’s easy to stuff an 8-bit value into a 16-bit type with no loss of data.
  • The language will do these conversions for you automatically. These are called implicit conversions.
  • When converting from a source type with fewer bits to a target type with more bits, the extra bits in the target need to be filled with either 0s or 1s.
  • When converting from a smaller unsigned type to a larger unsigned type, the extra most significant bits of the target are filled with 0s. This is called zero extension.
  • For conversion between signed types, the extra most significant bits are filled with the sign bit of the source expression.

Explicit Conversions and Casting

When you convert from a shorter type to a longer type, it’s easy for the longer type to hold all the bits of the shorter type. In other situations, however, the target type might not be able to accommodate the source value without loss of data.

For example, suppose you want to convert a ushort value to a byte.
  • A ushort can hold any value between 0 and 65,535.
  • A byte can only hold a value between 0 and 255.
  • As long as the ushort value you want to convert is less than 256, there won’t be any loss of data. If it is greater, however, the most significant bits will be lost.
For example, following figure shows an attempt to convert a ushort with a value of 1,365 to a byte, resulting in a loss of data. Not all the significant bits of the source value fit into the target type, resulting in an overflow and loss of data. The source value was 1,365, but the maximum ] value the target can hold is 255. The resulting value in the byte is 85 instead of 1,365.

Casting

For the predefined types, C# will automatically convert from one data type to another—but only between those types for which there is no possibility of data loss between the source type and the target type. That is, the language does not provide automatic conversion between two types if there is any value of the source type that would lose data if it were converted to the target type. If you want to make a conversion of this type, you must use an explicit conversion called a cast expression.

The following code shows an example of a cast expression.
static void Main(string[] args)
 {
  ushort sh = 10;
  byte sb = (byte)sh;
  Console.WriteLine("sb: " + sh + " = 0x{0:X}", sb);

  sh = 1365;
  sb = (byte)sh;
  Console.WriteLine("sb: " + sh + " = 0x{0:X}", sb);
 }

The output of the code in the figure shows both the decimal and hexadecimal values of the results and is the following:

sb: 10 = 0xA sb: 85 = 0x55

The checked and unchecked Operators

The checked and unchecked operators control the overflow checking context of an expression, which is placed between a set of parentheses. The expression cannot be a method. The syntax is the following:
checked ( [Expression] )
 unchecked ( Expression )
For example, the following code executes the same conversion—first in a checked operator and then in an unchecked operator.
ushort sh = 2000;
byte sb;
sb = unchecked ( (byte) sh ); // Most significant bits lost
Console.WriteLine("sb: {0}", sb);
sb = checked ( (byte) sh ); // OverflowException raised
Console.WriteLine("sb: {0}", sb);

This code produces the following output:
sb: 208
Unhandled Exception: System.OverflowException: Arithmetic operation resulted in an overflow. at Test1.Test.Main() in C:\Programs\Test1\Program.cs:line 21
  • In the unchecked context, the overflow is ignored, resulting in the value 208.
  • In the checked context, an OverflowException exception is raised.

Common Exception Type

Common Exception Types

The following exception types are used widely throughout the CLR and .NET Framework. You can throw these yourself or use them as base classes for deriving custom exception types.
Class Description
System.ArgumentException Thrown when a function is called with a bogus argument. This generally indicates a program bug.
System.ArgumentNullException Subclass of ArgumentException that’s thrown when a function argument is (unexpectedly) null.
System.ArgumentOutOfRangeException Subclass of ArgumentException that’s thrown when a (usually numeric) argument is too big or too small. For example, this is thrown when passing a negative number into a function that accepts only positive values.
System.InvalidOperationException Thrown when the state of an object is unsuitable for a method to successfully execute, regardless of any particular argument values. Examples include reading an unopened file or getting the next element from an enumerator where the underlying list has been modified partway through the iteration.
System.NotSupportedException Thrown to indicate that a particular functionality is not supported. A good example is calling the Add method on a collection for which IsReadOnly returns true.
System.NotImplementedException Thrown to indicate that a function has not yet been implemented.
System.ObjectDisposedException Thrown when the object upon which the function is called has been disposed.
System.NullReferenceException Thrown when attempting to access a member of an object whose value is null (indicating a bug in your code).