13.07.2015 Views

C# in Depth

C# in Depth

C# in Depth

SHOW MORE
SHOW LESS
  • No tags were found...

Create successful ePaper yourself

Turn your PDF publications into a flip-book with our unique Google optimized e-Paper software.

Covariance and contravariance143some random data, and then use that method as the action for a StreamFactory delegate<strong>in</strong>stance.List<strong>in</strong>g 5.3Demonstration of covariance of return types for delegatesdelegate Stream StreamFactory();static MemoryStream GenerateRandomData(){byte[] buffer = new byte[16];new Random().NextBytes(buffer);return new MemoryStream(buffer);}...StreamFactory factory = GenerateRandomData;B Declares delegate type return<strong>in</strong>g StreamCDeclares methodreturn<strong>in</strong>g MemoryStreamDConverts methodgroup with covarianceStream stream = factory();<strong>in</strong>t data;while ( (data=stream.ReadByte()) != -1){Console.WriteL<strong>in</strong>e(data);}E InvokesdelegateThe actual generation and display of the data <strong>in</strong> list<strong>in</strong>g 5.3 is only present to give the codesometh<strong>in</strong>g to do. (In particular, the way of generat<strong>in</strong>g random data is pretty awful!)The important po<strong>in</strong>ts are the annotated l<strong>in</strong>es. We declare that the delegate type has areturn type of Stream B, but the GenerateRandomData method C has a return type ofMemoryStream. The l<strong>in</strong>e creat<strong>in</strong>g the delegate <strong>in</strong>stance D performs the conversion wesaw earlier and uses covariance of return types to allow GenerateRandomData to be usedfor the action for StreamFactory. By the time we <strong>in</strong>voke the delegate <strong>in</strong>stance E, thecompiler no longer knows that a MemoryStream will be returned—if we changed the typeof the stream variable to MemoryStream, we’d get a compilation error.Covariance and contravariance can also be used to construct one delegate <strong>in</strong>stancefrom another. For <strong>in</strong>stance, consider these two l<strong>in</strong>es of code (which assume an appropriateHandleEvent method):EventHandler general = new EventHandler(HandleEvent);KeyPressEventHandler key = new KeyPressEventHandler(general);The first l<strong>in</strong>e is valid <strong>in</strong> <strong>C#</strong> 1, but the second isn’t—<strong>in</strong> order to construct one delegatefrom another <strong>in</strong> <strong>C#</strong> 1, the signatures of the two delegate types <strong>in</strong>volved have to match.For <strong>in</strong>stance, you could create a MethodInvoker from a ThreadStart—but youcouldn’t do what we’re do<strong>in</strong>g <strong>in</strong> the previous code. We’re us<strong>in</strong>g contravariance to createa new delegate <strong>in</strong>stance from an exist<strong>in</strong>g one with a compatible delegate type signature,where compatibility is def<strong>in</strong>ed <strong>in</strong> a less restrictive manner <strong>in</strong> <strong>C#</strong> 2 than <strong>in</strong> <strong>C#</strong> 1.This new flexibility <strong>in</strong> <strong>C#</strong> 2 causes one of the very few cases where exist<strong>in</strong>g valid<strong>C#</strong>1 code may produce different results when compiled under <strong>C#</strong> 2: if a derived classoverloads a method declared <strong>in</strong> its base class, a delegate creation expression that previouslyonly matched the base class method could now match the derived classmethod due to covariance or contravariance. In this case the derived class methodwill take priority <strong>in</strong> <strong>C#</strong> 2. List<strong>in</strong>g 5.4 gives an example of this.Licensed to Rhona Hadida

Hooray! Your file is uploaded and ready to be published.

Saved successfully!

Ooh no, something went wrong!