Covariance and Contravariance for Generic Types

Một phần của tài liệu Visual C 2012 How to Program _ www.bit.ly/taiho123 (Trang 881 - 884)

2. When the app executes, another compiler (known as the just-in-time compiler

21.6 Covariance and Contravariance for Generic Types

C# supportscovarianceandcontravarianceof generic interface and delegate types. We’ll consider these concepts in the context of arrays, which have always been covariant and contravariant in C#.

21.6 Covariance and Contravariance for Generic Types 841

Covariance in Arrays

Recall ourEmployeeclass hierarchy from Section 12.5, which consisted of the base class

Employee and the derived classes SalariedEmployee, CommissionEmployeeand Base-

PlusCommissionEmployee. Assuming the declarations

we can write the following statement:

Even though the array type SalariedEmployee[] does not derive from the array type

Employee[], the preceding assignmentisallowed because classSalariedEmployeeis a de- rived class ofEmployee.

Similarly, suppose we have the following method, which displays thestringrepre- sentation of eachEmployeein itsemployeesarray parameter:

We can call this method with the array ofSalariedEmployees, as in:

and the method will correctly display thestringrepresentation of eachSalariedEmploy- eeobject in the argument array. Assigning an array of a derived-class type to an array vari- able of a base-class type is an example ofcovariance.

Covariance in Generic Types

Covariance works with several generic interface and delegate types, including IEnumera-

ble<T>. Arrays and generic collections implement theIEnumerable<T>interface. Using thesalariedEmployeesarray declared previously, consider the following statement:

Prior to Visual C# 2010, this generated a compilation error. InterfaceIEnumerable<T>is now covariant, so the preceding statementisallowed. If we modify methodPrintEmploy- eesas in:

we can callPrintEmployeeswith the array ofSalariedEmployeeobjects, because that ar- ray implements the interfaceIEnumerable<SalariedEmployee>and because aSalaried-

Employeeis anEmployeeand becauseIEnumerable<T>is covariant. Covariance like this worksonlywithreferencetypes that are related by a class hierarchy.

Contravariance in Arrays

Previously, we showed that an array of a derived-class type (salariedEmployees) can be assigned to an array variable of a base-class type (employees). Now, consider the following statement, which hasalwaysworked in C#:

SalariedEmployee[] salariedEmployees = {

new SalariedEmployee( "Bob", "Blue", "111-11-1111", 800M ), new SalariedEmployee( "Rachel", "Red", "222-22-2222", 1234M ) };

Employee[] employees;

employees = salariedEmployees;

void PrintEmployees( Employee[] employees )

PrintEmployees( salariedEmployees );

IEnumerable< Employee > employees = salariedEmployees;

void PrintEmployees( IEnumerable< Employee > employees )

SalariedEmployee[] salariedEmployees2 = ( SalariedEmployee[] ) employees;

Based on the previous statements, we know that theEmployeearray variableemployees currently refers to an array ofSalariedEmployees. Using acast operatorto assignemploy-

ees—an array of base-class-type elements—to salariedEmployees2—an array of de- rived-class-type elements—is an example ofcontravariance. The preceding cast will fail at runtime ifemployeesisnotan array ofSalariedEmployees.

Contravariance in Generic Types

To understandcontravariancein generic types, consider aSortedSetofSalariedEmploy- ees. ClassSortedSet<T>maintains a set of objects in sorted order—no duplicates are al- lowed. The objects placed in aSortedSetmustimplement theIComparable<T>interface.

For classes thatdo notimplement this interface, you can still compare their objects using an object that implements theIComparer<T>interface. This interface’sComparemethod com- pares its two arguments and returns 0 if they’re equal, anegativeinteger if the first object is less than the second, or apositiveinteger if the first object is greater than the second.

OurEmployeehierarchy classes donotimplementIComparable<T>. Let’s assume we wish to sortEmployees by social security number. We can implement the following class to compareanytwoEmployees:

MethodComparereturns the result of comparing the twoEmployees social security num- bers usingstringmethodCompareTo.

Now consider the following statement, which creates aSortedSet:

When the type argument doesnotimplementIComparable<T>, you must supply an ap- propriateIComparer<T>object to compare the objects that will be placed in theSorted-

Set. Since, we’re creating aSortedSetofSalariedEmployees, the compiler expects the

IComparer<T>object to implement theIComparer<SalariedEmployee>. Instead, we pro- vided an object that implementsIComparer<Employee>. The compiler allows us to pro- vide anIComparer for a base-class type where anIComparerfor a derived-class type is expected because interfaceIComparer<T>supports contravariance.

Web Resources

For a list of covariant and contravariant interface types, visit

It’s also possible to create your own variant types. For information on this, visit

class EmployeeComparer : IComparer< Employee >

{

int IComparer< Employee >.Compare( Employee a, Employee b) {

return a.SocialSecurityNumber.CompareTo(

b.SocialSecurityNumber );

} // end method Compare } // end class EmployeeComparer

SortedSet< SalariedEmployee > set =

new SortedSet< SalariedEmployee >( new EmployeeComparer() );

msdn.microsoft.com/en-us/library/dd799517.aspx#VariantList

msdn.microsoft.com/en-us/library/dd997386.aspx

Một phần của tài liệu Visual C 2012 How to Program _ www.bit.ly/taiho123 (Trang 881 - 884)

Tải bản đầy đủ (PDF)

(1.020 trang)