Pointers can also be declared of type void

advertisement
533570690 Advanced C#
Page 1 of 33
Chapters 6 and 7
Delegates and Events
Memory Management and Pointers

How the runtime allocates space on the stack and the heap

How garbage collection works

How to use destructors and the System.IDisposable interface to ensure
unmanaged resources are released correctly

The syntax of using pointers in C#
How to use pointers to implement high-performance stack-based arrays
533570690 Advanced C#
Page 2 of 33
Background:
Call Back Functions
Passing Functions as parameters - general Example:
int G ( int t , int f
( int x )
) // function parameter - function return
// type int function name f
// parameter list - int x
{
// compute f ( t ) using function f and the parameter t.
// return t. Return the product of this value and t
return t * f ( t ) ;
}
call back occurs
// xsquared is a function with an integer parameter x
// and an integer return value
here
int xsquared ( int x )
{
return x * x;
}
Y = G ( 3, xsquared )
t and
// client calls function G with an integer parameter
// and the function xsquared
cout << G ( 3.0, xsquared ) << endl; // cout - prints output 27
Name of function is the address
of the function
Disadvantage: no type safety -
533570690 Advanced C#
Page 3 of 33
Delegates
Delegates are objects similar to classes – used to wrap methods
These wrapped methods are then passed to other methods
Advantage:
Type Safety – also in .NET methods are typically inside an object
They are typically associated with an instance
Uses:

Starting Threads
void EntryPoint( )
{
thread performs some taks
}
Thread NewThread = new Thread()
Thread.Start(EntryPoint);
// direct approach isWRONG

Generic Library Classes

Events
533570690 Advanced C#
Page 4 of 33
Using Delegates in C#
Single and multicast delegates
Singe Cast delegates wrap one method – similar to defining a class
Syntax examples:
delegate void VoidOperation(unit x);
delegate double TwoLongsOp(long first, long second);
delegate string GetAString();
Note: a delegate type gives a name to a method
Terminalogy: The instance of a class is referred to as an object
The instance of a delegate is referred to as a delegate
An instance of a given delegate can refer to any instance or static
method on any object of any type, provided that the signature of the
method matches the signature of the delegate.
533570690 Advanced C#
Page 5 of 33
delegate
namespace SimpleDelegate
{
delegate double DoubleOp(double x);
class MainEntryPoint
{
instantiate delegates
static void Main()
methods are passed to
{
delegate constructor
DoubleOp [] operations =
{
new DoubleOp(MathsOperations.MultiplyByTwo),
new DoubleOp(MathsOperations.Square)
};
pass delegate and value to a
a method
for (int i=0 ; i<operations.Length ; i++)
{
Console.WriteLine("Using operations[{0}]:", i);
ProcessAndDisplayNumber(operations[i], 2.0);
ProcessAndDisplayNumber(operations[i], 7.94);
ProcessAndDisplayNumber(operations[i], 1.414);
Console.WriteLine();
}
Console.ReadLine();
}
static void ProcessAndDisplayNumber(DoubleOp action, double value)
{
//Call back occurs here
double result = action(value);
Console.WriteLine("Value is {0}, result of operation is {1}", value,
result);
}
}
533570690 Advanced C#
Page 6 of 33
class MathsOperations
{
public static double MultiplyByTwo(double value)
{
return value*2;
}
method signatures must
match the signature of the delegate
public static double Square(double value)
{
return value*value;
}
}
}
OUTPUT:
Using
Value
Value
Value
operations[0]:
is 2, result of operation is 4
is 7.94, result of operation is 15.88
is 1.414, result of operation is 2.828
Using
Value
Value
Value
operations[1]:
is 2, result of operation is 4
is 7.94, result of operation is 63.0436
is 1.414, result of operation is 1.999396
533570690 Advanced C#
Page 7 of 33
Multicast Delegates



It is possible for a delegate to wrap more than one method
Such delegates are called multicast delegate
The method return type must be of type void ( because methods
can return different data)
The wrapped methods are called in sequence
Example:
// return type void assumes
delegate void DoubleOp(double value); multicast
class MainEntryPoint
{
static void Main()
instantiate delegate
{
DoubleOp operations =
new DoubleOp(MathsOperations.MultiplyByTwo);
operations += new DoubleOp(MathsOperations.Square);
add another delegate
operator += is overloaded
ProcessAndDisplayNumber(operations, 2.0);
ProcessAndDisplayNumber(operations, 7.94);
ProcessAndDisplayNumber(operations, 1.414);
Console.WriteLine();
Console.ReadLine();
}
static void ProcessAndDisplayNumber(DoubleOp action, double value)
{
Console.WriteLine("\nProcessAndDisplayNumber called with value = "
+ value);
action(value); // call back occurs here
}
}
533570690 Advanced C#
Page 8 of 33
// Methods wrapped by delegate
class MathsOperations
{
public static void MultiplyByTwo(double value)
{
double result = value*2;
Console.WriteLine("Multiplying by 2: {0} gives {1}", value, result);
}
public static void Square(double value)
{
double result = value*value;
Console.WriteLine("Squaring: {0} gives {1}", value, result);
}
}
}
OUTPUT:
ProcessAndDisplayNumber called with value = 2
Multiplying by 2: 2 gives 4
Squaring: 2 gives 4
ProcessAndDisplayNumber called with value = 7.94
Multiplying by 2: 7.94 gives 15.88
Squaring: 7.94 gives 63.0436
ProcessAndDisplayNumber called with value = 1.414
Multiplying by 2: 1.414 gives 2.828
Squaring: 1.414 gives 1.999396
533570690 Advanced C#
Page 9 of 33
EVENTS
A form of communications between objects
Windows Applications are message based
A detailed example - Structure of a sample application:
Consumer:
MessageDisplayer
3
Event Generator:
UserInputMonitor
1
UserRequestEventArgs
2
Event Handler:
UserRequestHandler
Event:
OnUserRequest
Consumer:
ManagersStaffMonitor
4
UserRequestEventArgs
Event Handler:
UserRequestHandler
Main Program
Note: classic object oriented design - the main program does nothing
except instantiates the various objects that make up the application
Delegates are used as a means of wiring the event up when the message
is received by the application
The event receiver can by any application
The sender’s job is to raise the event
533570690 Advanced C#
Page 10 of 33
Using System;
Using System.Windows.Forms;
Namespace SomeNameSpace
{
classMainEntryPoint
{
static void Main();
{
UserInputMonitor inputMonitor = new UserInputMonitor();
MessageDisplayer inputProcessor =
new MessageDisplayer(inputMonitor);
ManagersStaffMonitor Mortimer =
new ManagersStaffMonitor(inputMonitor);
InputMonitor.Run ( ); // start application
}
}
}
533570690 Advanced C#
Page 11 of 33
1. Define the event argument, i.e. the object that is passed to the event
handler
// enumerated request types
enum RequestType {AdRequest, PersonalMessageRequest);
class UserRequestEventArgs: EventArgs // must be derived from EventArgs
{
private RequestType request;
// constructor
public UserRequestEventArgs(RequestType request): base ( )
{
this.request request;
}
// readonly property for private data request
public RequestType Request
{
get
{
return request;
}
}
}
533570690 Advanced C#
Page 12 of 33
2. Implement UserInputMonitor – This is the object that deals with user
input. It will ask the user which message they want to see
class UserInputMonitor
{
// define delegate and method signature to call back event handlers
public delegate void UserRequest(object sender,
UserRequestEventArgs e) ;
// tell compiler that this class contains a member event, of a type given
// by the delegate
public event UserRequest OnUserRequest;
//method that fires event
public void Run( )
{
bool finished = false;
do
{
Console.WriteLine(“Select preferred option:”);
Console.WriteLine(“ Request advertisement – hit A then Return”);
Console.WriteLine)” Request personal messge from Mortimer “ +
“= hit P then Return”);
Console.WriteLine( “ Exit = hit X then Return”);
string response = Console.ReadLine();
char responseChar = (response == “ “ ? ‘ ‘ : char.ToUpper(response[0]);
switch (responseChar)
{
case ‘X’:
finished = true; break;
case ‘A’:
OnUserRequest(this, new
UserRequestEventArgs(RequestType.AdRequest));
break;
case ‘P’:
OnUserRequest(this, new
UserRequestEventArgs(RequestType.PersonalMessageRequest));
break;
}
}while (!finished);
} // end of class
}
533570690 Advanced C#
Page 13 of 33
3. Implement MessageDisplayer – object that is responsible for
displaying the appropriate message
class MessageDisplayer
{
// constructor adds delegate – MessageDisplayer wants to be notified
// so it can display the appropriate message
public MessageDisplayer(UserInputMonitor monitor)
{
monitor.OnUserRequest += new
UserInputMonitor.UserRequest(UserRequestHandler);
}
// Event handler
protected void UserRequestHandler(object sender,
UserRequestEventArgs e) // event argument
{
switch (e.Request)
{
case RequestType.AdRequest: // enumerated type
Console.WriteLine(“Mortimer Phones is better than anyone “ +
“else because all our software is written in C#!\n”);
break;
case RequestType.PersonalMessageRequest:
Console.WriteLine(“Today Mortimer issued the following “ +
“statement: \n Nevermore!\n”;
break;
}
}
}
533570690 Advanced C#
Page 14 of 33
4. Implement ManagerStaffMonitor – the object that will like to be
notified whenever someone has asked to see his personal message
( does not care about ad requests)
class MangerStaffMonitor
{
// constructor adds delegate – ManagerStaffMonitor wants to be
// notified so it can display the message Box
public ManagerStaffMonitor(UserInputMonitor monitor)
{
monitor.OnUserRequest +=
new UserInputMonitor.UserRequest(UserRequestHandler);
}
// Event handler
protected void UserRequestHandler(object sender,
UserRequestEventArgs e) // event argument
{
if ( e.Requst == RequestType.PersonalMessageRequest)
{
MessageBox.Show(“Kaark!”, “Mortimer says …”);
}
}
Sample Output given input A and P :
Select preferred option:
Request advertisement – hit A then Return
Request personal message from Mortimer – hit P then Return
Exit – hit X then Return
A
Mortimer Phones is better than anyone else because all our software is written in C#!
Select preferred option:
Request advertisement – hit A then Return
Request personal message from Mortimer – hit P then Return
Exit – hit X then Return
P
Today Mortimer issued the following statement:
Nevermore!
Message Box
Mortimer says …
Kaark!
OK
533570690 Advanced C#
Page 15 of 33
Generating Events
using System;
using System.IO;
using System.ComponentModel;
namespace SimpleEvent
{
/// <summary>
/// Summary description for BusEntity.
/// </summary>
public class BusEntity
{
string _time = "";
public BusEntity()
{
// Adds event handler - passing method to handle the
event
Form1.Action += new
Form1.ActionEventHandler(EventManager_Action);
}
private void EventManager_Action(object sender,
ActionCancelEventArgs ev)
{
ev.Cancel = !DoActions();
// change text in event args ev object
if(ev.Cancel)
ev.Message = "Wasn't the right time.";
}
private bool DoActions()
{
bool retVal = false;
DateTime tm = DateTime.Now;
if(tm.Second < 30)
{
_time = "The time is " + DateTime.Now.ToLongTimeString();
retVal = true;
}
else
_time = "";
return retVal;
}
public string TimeString
{
get {return _time;}
}
533570690 Advanced C#
}
}
Page 16 of 33
533570690 Advanced C#
Page 17 of 33
using System;
using System.IO;
using System.ComponentModel;
namespace SimpleEvent
{
/// <summary>
/// Summary description for BusEntity.
/// </summary>
public class BusEntity
{
string _time = "";
public BusEntity()
{
// Adds event handler - passing method to handle the
event
Form1.Action += new
Form1.ActionEventHandler(EventManager_Action);
}
private void EventManager_Action(object sender,
ActionCancelEventArgs ev)
{
ev.Cancel = !DoActions();
// change text in event args ev object
if(ev.Cancel)
ev.Message = "Wasn't the right time.";
}
private bool DoActions()
{
bool retVal = false;
DateTime tm = DateTime.Now;
if(tm.Second < 30)
{
_time = "The time is " + DateTime.Now.ToLongTimeString();
retVal = true;
}
else
_time = "";
return retVal;
}
public string TimeString
{
get {return _time;}
}
}
}
533570690 Advanced C#
Page 18 of 33
533570690 Advanced C#
Page 19 of 33
Preprocessor Directives
No longer important in C# - since there is no pre-processor – However, The
C# compiler recognizes # directives
#define and #undef
- define and undefine labels
#if, #elif, #else, and #endif - used to inform compiler whether or not to
compile a block of code
#warning and #error - raises warnings and errors when the compiler
encounters them
#if DEBUG && RELEASE
#error “ Some message to yourself
#endif
#region and #endregion - directive allows blocks of code to be treated as a
singe block with a given name – editors can
recognize these directives and provide better code
layout on the screen
#line – a directive used to alter line number information that is output by the
compiler in warnings and error messages ( used when compiling
code together with other packages that alters the code before
compilation) so that line numbers and file names match.
533570690 Advanced C#
Page 20 of 33
Attributes
- Attributes are defined in the .NET Framework base classes that serve as
a directive to the compiler (similar to pre-processor directives) –
- They can cause extra data to be provided of the compiled assembly which
can be examined by using the “technique of reflection”
- There is no limit to attributes since the user can define their own attributes
- Attributes are markers that can be applied to a method or class
Some Base .NET Class attributes:
Conditional –prevent compiler from compiling the statements
[Conditional (“DEBUG”)]
public void DoSomeDebugStuff()
{
// do something
}
DllImport – to access basic Windows API or old-style functions – used to
mark a method as being defined in an external DLL rather than in any
assembly
Obsolete – To mark a method that is obsolete – depending on the setting
applied to the attribute, the compiler will generate a warning or an error
533570690 Advanced C#
Page 21 of 33
Memory Management
The fundamental type is System.Object
Object
Value Type
Class
Byte
Interface
Int16
UInt16
Single
Double
Array
Char
String
Int32
UInt32
Int64
UInt64
Delegate
Decimal
Structure
Others
Boolean
Others
Others
Reference
Types
STACK
Managed Heap
Enum
533570690 Advanced C#
Page 22 of 33
Value Data types – using the Sytem Stack:
- virtual memory – memory addressing handled by the OS –
mapping memory address seeing by the program to actual location in
hardware memory or on disk
Stack Pointer – tells the address of the next free location (downward from
high memory to low) – used to store value types and copy parameters
during function calls
Reference Data Types – use the managed heap (managed by the CLR)
Successive objects are placed on the heap using a pointer to indicate the next
free location
When the garbage collector cleans up it moves free space to the end of the
heap and updates all references for objects on the heap
Instantiating reference types is now faster – offsetting the overhead
encountered when the garbage collector runs
533570690 Advanced C#
Page 23 of 33
Garbage Collection and Freeing Unmanaged Resources

All objects are on the heap and referenced by the client code

All objects are removed from memory by the garbage collector.
There can be a delay between the reference going out of scope and the
object being deleted. The garbage collector runs independent of the
code and the collection of an object is non-deterministic. However,
The garbage collector can be forced to run using System.GC.Collect( )
method.

C# does support destructors. A destructor may be defined, which
will be automatically invoked when the object is garbage-collected
The underlying .NET architecture the method Finalize() is compiled
replacing the user defined destructor.

In general, a destructor slows down the garbage collection since it
takes two passes by the garbage collector (GC)to destroy the object.
The first one calls the destructor (Finalize()) and the second one
removes the object. The destructor should only be used as a backup.

When the destructor is called the unmanaged resources can be
cleaned up. We know the destructor has been called by the garbage
collector which will clean up the managed resources, so that the
destructor only has to clean up unmanaged resources ( ex: closing a
file). Since destruction on unmanaged resources is not deterministic,
the destructor cannot reference managed resources anyway, so there is
no point in letting a destructor free up managed resource.

If resources need to be cleaned up immediately and cannot wait for
the garbage collection to occur, or the objects of some class contain
references to other managed objects that are large and should be
cleaned up as soon as the object containing the references is no longer
needed, the class should implement the IDisposable.Dispose ( )
method.

If a client calls Idisposable.Dispose() then that client indicates that all
resources associated with that object ( managed an unmanaged)
should be cleaned up.
533570690 Advanced C#
Page 24 of 33
The System.IDisposable Interface
A recommended alternative to using a destructor
Your class should inherit
interface IDisposable
class MyClass : IDisposable
{
// IDisposable declares a single method nameded Dispose();
// we implement this method in MyClass
public void Dispose()
{
// implementation
}
}
Example – Class ResourceGobbler ( uses some resources)
ResourceGobbler theInstance = new ResourceGobbler();
// do some processing
theInstance.Dispose(); // call the instance method Dispose to
// clean up resources
This code will fail if an exception occurs and should be placed in a try block
ResourceGobbler theInstance = null;
// ensures that Dispose is always called on theInstance
try
{
ResourceGobbler theInstance = new ResourceGobbler();
// do some processing……….
}
catch
{
// call the instance method Dispose to clean up resources
if (theInstance != null) theInstance.Dispose();
}
533570690 Advanced C#
Page 25 of 33
A more effective way to accomplish this is using the keyword using
( a different context here as when using the keyword using for namespaces)
The following code generates Intermediate Language IL equivalent to the try
block
using (ResourceGobbler theInstance = new ResourceGobbler())
{
// do some processing
}
The instance theInstance is scoped to the block below the using statement
When the variable theInstance goes out of scope the Dispose method will be
called automatically even if exceptions occur
If you have a try block – you can also call Dispose in the Finally Clause
which always gets executed.
Implementing IDisposable and a Destructor –
In the example on the next page, the consumer calls Dispose() –
indicating that all managed and unmanged resources need to be cleaned
up
If the GC calls Finalize(). I.e. the ~ResourceHolder (destructor
– nothing needs to be cleaned up
Note:
- destructors have no return type, no parameters and no access modifier.
- each class writes it’s own destructor
- The difference between Close() and Dispose()
Close() closes resources that can be opened again ( ex: files)
Dispose () the client is finished using the resource
533570690 Advanced C#
Page 26 of 33
public class ResourceHolder : IDisposable
{
private bool isDisposed = false;
public void Dispose()
{
Dispose(true);
// implement Dispose without parameter
//call Dispose with bool true
// tell GC that destructor no longer needs to be called
// more efficient – unmanged objects are already cleaned up
GC.SuppressFinalize(this); // don’t call destructor
}
protected virtual void Dispose(bool disposing)// with param
{
if (!isDisposed)
{
if (disposing)
{
//cleanup managed objects by calling their Dispose() methods
}
// cleanup unmanged objects
}
isDisposed = true;
}
~ResourceHolder() // destructor called by GC Finalize ()
{
Dispose (false); // call dispose with Boolean false
}
} // end of class
533570690 Advanced C#
Page 27 of 33
Unsafe Code
Pointers –
Advantages:
Powerful and flexible tool
Performance –
Backwards compatibility
Debugging - Make memory addresses available to the user
Desadvantages:
The syntax is complex
Pointers, when not used carefully can cause bugs in your code
Easy to overwrite memory
.NET type safety checks are bypassed
533570690 Advanced C#
Page 28 of 33
unsafe
Due to the risk associated with pointers - code using pointers is surrounded
with the keyword unsafe
unsafe int GetSomeNumber(0
{
// code within method can use pointers
}
unsafe class SomeClass
{
// any method in the class can use pointers
}
void SomeMethod()
{
unsafe
{
// block within method can now use pointers
}
}
// A variable itself cannot be declared unsave
unsafe int *pX; // not possible
Compiling unsafe code require compiler option
/unsafe
533570690 Advanced C#
Page 29 of 33
Pointer Syntax

& address operator

dereference opeator ( get the contents of this address)
int x = 10;
int *pX , *py;
pX = &x;
pY =pX;
*pY = 20;
Value of x 
20
A pointer can be declared to any value type but not to an array or class.
Pointers can only be declared and point to unmanaged data types
The garbage collector handles CLR all managed types
Casting pointers to integer types- Pointers are addresses stored as integers
int x = 10;
int *pX , *py;
pX = &x;
pY =pX;
*pY = 20;
uint y = (uint)pX; // cats pointer pX to an integer and store as an integer
int *pD = (int*)y; // cast int y to an address and store as a pointer
Pointers take 4 bytes, thus they can only be cast to an uint, long, or ulong
Note: Integer value must be withing memory address range
64-bit processors will occupy 8 bytes – will certainly cause overflow
Use checked context to avoid overflow
533570690 Advanced C#
Page 30 of 33
Casting between Pointers
byte aByte = 8;
byte *pByte = &aByte;
double pDouble = (double)pBytee; // legal – but could lead to problems
Pointers can also be declared of type void
void *pointerToVoid;
pointerToVoid = (void*)pointerToInt;
The sizeof Operator - takes the type as parameter and returns the size in
bytes
Can be used in built-in types and struct
For struct the size will depend on the fields within a struct
sizeof cannot be used on classes
sizeof(sbyte)
=1
Pointer Arithmetic ( connot be done on pointers of type void)
Operators +, -, +=, -=, ++, and -- can be used
When dereferencing contiguous memory, Scaling will depend on the pointer
type - to dereference
General : - Ex: P is of type T and X is added to P then the arithmetic is as
follows:
P + X *(sizeof(T))
533570690 Advanced C#
Pointers to Structs
Work the same way as pointers to value types – except
struct cannot contain a reference type
struct MyStruct
{
public long X;
public float F;
}
MyStruct * pStruct;
MyStruct S = newMyStruct();
dereference operators *. Or ->
pStruct = &S;
*(pStruct).X = 4; OR pStruct->X = 4;
Pointers to structure members:
long *pL = &(S.X);
float * pF = & ( pStruct->F) ;
Page 31 of 33
533570690 Advanced C#
Page 32 of 33
Pointers to Class Members – using fixed keyword
Assigning a pointer to a member of a class in a regular way would produce a
compiler error, because a class is a CLR managed object it is garbage
collected and the memory pointed to by pointers is not garbage collected
A special syntax is required that tells the garbage collector that there may be
pointers pointing to members of certain class instances
MyClass MyClassObj = new MyClass();
fixed ( long *pObject = &(myClassObject.X)
{
// object will not be moved until block exits
}
multiple fixed statements may be declared before the block
fixed ( long *pX = &(myClassObject.X))
fixed (float *pF =&(myClassObject.F))
{
}
fixed ( long *pX = &(myClassObject.X))
{
fixed (float *pF = &(myClassObject.F)
{
}
}
MyClass MyClassObj = new MyClass();
MyClass myClassObj2 = new MyClass(0;
fixed ( long *pX = &(myClassObject.X), pX2 = &(myClassObj2.X))
{
// object will not be moved until block exits
}
533570690 Advanced C#
Page 33 of 33
Example:
Allocating an array on the stack using a pointer
class MainEntryPoint
{
static unsafe void Main()
{
Console.Write(“How big an array do you want?”;
string userInput = Console.ReadLine();
uint size = uint.Parse(userinput);
long *pArray = stackalloc long ( (int)size);
for ( int i = 0; i < size; i ++ )
pArray[i]= i * i;
Store values
using brackets
Access values
using pointer
arithmetic
for ( int i = 0; i < size; i ++ )
Console.WriteLine(“Element {0} = (i)”, I, + * ( pArray + i ));
}
}
Download