Home Random Page


CATEGORIES:

BiologyChemistryConstructionCultureEcologyEconomyElectronicsFinanceGeographyHistoryInformaticsLawMathematicsMechanicsMedicineOtherPedagogyPhilosophyPhysicsPolicyPsychologySociologySportTourism






Nbsp;   Casting Between Types

One of the most important features of the CLR is type safety. At run time, the CLR always knows what type an object is. You can always discover an object’s exact type by calling the GetType method.

Because this method is nonvirtual, it is impossible for a type to spoof another type. For example, the

Employee type can’t override the GetType method and have it return a type of SuperHero.

 

Developers frequently find it necessary to cast an object to various types. The CLR allows you to cast an object to its type or to any of its base types. Your choice of programming language dictates how to expose casting operations to the developer. For example, C# doesn’t require any special syn- tax to cast an object to any of its base types, because casts to base types are considered safe implicit conversions. However, C# does require the developer to explicitly cast an object to any of its derived types because such a cast could fail at run time. The following code demonstrates casting to base and derived types.

 

// This type is implicitly derived from System.Object. internal class Employee {

...

}

 

public sealed class Program { public static void Main() {

// No cast needed since new returns an Employee object

// and Object is a base type of Employee. Object o = new Employee();


// Cast required since Employee is derived from Object.

// Other languages (such as Visual Basic) might not require

// this cast to compile. Employee e = (Employee) o;

}

}

 

This example shows what is necessary for your compiler to compile your code. Now I’ll explain what happens at run time. At run time, the CLR checks casting operations to ensure that casts are al- ways to the object’s actual type or any of its base types. For example, the following code will compile, but at run time, an InvalidCastException will be thrown.

 

internal class Employee {

...

}

internal class Manager : Employee {

...

}

 

public sealed class Program { public static void Main() {

// Construct a Manager object and pass it to PromoteEmployee.

// A Manager IS­A Object: PromoteEmployee runs OK. Manager m = new Manager();

PromoteEmployee(m);

 

// Construct a DateTime object and pass it to PromoteEmployee.

// A DateTime is NOT derived from Employee. PromoteEmployee

// throws a System.InvalidCastException exception. DateTime newYears = new DateTime(2013, 1, 1); PromoteEmployee(newYears);

}

 

 

public static void PromoteEmployee(Object o) {

// At this point, the compiler doesn't know exactly what

// type of object o refers to. So the compiler allows the

// code to compile. However, at run time, the CLR does know

// what type o refers to (each time the cast is performed) and

// it checks whether the object's type is Employee or any type

// that is derived from Employee. Employee e = (Employee) o;

...

}

}


In the Main method, a Manager object is constructed and passed to PromoteEmployee. This code compiles and executes because Manager is ultimately derived from Object, which is what Promote­



Employee expects. Once inside PromoteEmployee, the CLR confirms that o refers to an object that is either an Employee or a type that is derived from Employee. Because Manager is derived from Employee, the CLR performs the cast and allows PromoteEmployee to continue executing.

After PromoteEmployee returns, Main constructs a DateTime object and passes it to Promote­ Employee. Again, DateTime is derived from Object, and the compiler compiles the code that calls PromoteEmployee with no problem. However, inside PromoteEmployee, the CLR checks the cast and detects that o refers to a DateTime object and is therefore not an Employee or any type derived from Employee. At this point, the CLR can’t allow the cast and throws a System.InvalidCastException.

If the CLR allowed the cast, there would be no type safety, and the results would be unpredictable, including the possibility of application crashes and security breaches caused by the ability of types to easily spoof other types. Type spoofing is the cause of many security breaches and compromises an application’s stability and robustness. Type safety is therefore an extremely important part of the CLR.

By the way, the proper way to declare the PromoteEmployee method would be to specify an Employee type instead of an Object type as its parameter so that the compiler produces a compile- time error, saving the developer from waiting until a runtime exception occurs to discover a problem. I used Object so that I could demonstrate how the C# compiler and the CLR deal with casting and type-safety.

 

Casting with the C# is and as Operators

Another way to cast in the C# language is to use the is operator. The is operator checks whether an object is compatible with a given type, and the result of the evaluation is a Boolean: true or false. The is operator will never throw an exception. The following code demonstrates.

 

Object o = new Object();

Boolean b1 = (o is Object); // b1 is true. Boolean b2 = (o is Employee); // b2 is false.

 

If the object reference is null, the is operator always returns false because there is no object available to check its type.

The is operator is typically used as follows.

 

if (o is Employee) {

Employee e = (Employee) o;

// Use e within the remainder of the 'if' statement.

}


In this code, the CLR is actually checking the object’s type twice: The is operator first checks to see if o is compatible with the Employee type. If it is, inside the if statement, the CLR again verifies that o refers to an Employee when performing the cast. The CLR’s type checking improves security, but it certainly comes at a performance cost, because the CLR must determine the actual type of the object referred to by the variable (o), and then the CLR must walk the inheritance hierarchy, checking each base type against the specified type (Employee). Because this programming paradigm is quite com- mon, C# offers a way to simplify this code and improve its performance by providing an as operator.

 

Employee e = o as Employee; if (e != null) {

// Use e within the 'if' statement.

}

 

In this code, the CLR checks if o is compatible with the Employee type, and if it is, as returns a non-null reference to the same object. If o is not compatible with the Employee type, the as opera- tor returns null. Notice that the as operator causes the CLR to verify an object’s type just once. The if statement simply checks whether e is null; this check can be performed faster than verifying an object’s type.

The as operator works just as casting does except that the as operator will never throw an excep- tion. Instead, if the object can’t be cast, the result is null. You’ll want to check to see whether the resulting reference is null, or attempting to use the resulting reference will cause a System.Null­ ReferenceException to be thrown. The following code demonstrates.

 

Object o = new Object(); // Creates a new Object object Employee e = o as Employee; // Casts o to an Employee

// The cast above fails: no exception is thrown, but e is set to null. e.ToString(); // Accessing e throws a NullReferenceException.

To make sure you understand everything just presented, take the following quiz. Assume that

these two class definitions exist.

 

internal class B { // Base class

}

 

internal class D : B { // Derived class

}

 

Now examine the lines of C# code in Table 4-3. For each line, decide whether the line would com- pile and execute successfully (marked OK in Table 4-3), cause a compile-time error (CTE), or cause a run-time error (RTE).


TABLE 4-3Type-Safety Quiz

 

Statement OK CTE RTE
Object o1 = new Object();    
Object o2 = new B();    
Object o3 = new D();    
Object o4 = o3;    
B b1 = new B();    
B b2 = new D();    
D d1 = new D();    
B b3 = new Object();    
D d2 = new Object();    
B b4 = d1;    
D d3 = b2;    
D d4 = (D) d1;    
D d5 = (D) b2;    
D d6 = (D) b1;    
B b5 = (B) o1;    
B b6 = (D) b2;    

       
 
 
   

 

 


Date: 2016-03-03; view: 719


<== previous page | next page ==>
Nbsp;   All Types Are Derived from System.Object | Nbsp;   Namespaces and Assemblies
doclecture.net - lectures - 2014-2024 year. Copyright infringement or personal data (0.008 sec.)