Upload
irma-paul
View
221
Download
0
Embed Size (px)
DESCRIPTION
Item 22: Define Outgoing Interfaces with Events Item 22: Define Outgoing Interfaces with Events
Citation preview
Effective C#50 Specific Way to Improve Your C#
Item 22, 23
Agenda
• Item 22: Define Outgoing Interfaces with Events
• Item 23: Avoid Returning References to Internal Class Objects
• Item 22: Define Outgoing Interfaces with Events
Events and Delegates
• Events and delegates are the same things?– No, you can use delegates without defining events
Sample log class (1/3)
public class LoggerEventArgs : EventArgs{ public readonly string Message; public readonly int Priority; public LoggerEventArgs ( int p, string m ) { Priority = p; Message = m; }}
Sample log class (2/3)
// Define the signature for the event handler:public delegate void AddMessageEventHandler( object sender, LoggerEventArgs msg );
public class Logger{ static Logger( ) { _theOnly = new Logger( ); }
private Logger( ) { }
Sample log class (3/3) private static Logger _theOnly = null; public Logger Singleton { get { return _theOnly; } }
// Define the event: public event AddMessageEventHandler Log;
// add a message, and log it. public void AddMsg ( int priority, string msg ) { // This idiom discussed below. AddMessageEventHandler l = Log; if ( l != null ) l ( null, new LoggerEventArgs( priority, msg ) ); }}
Reference copy
public event field public class Logger{ private AddMessageEventHandler _Log;
public event AddMessageEventHandler Log { add { _Log = _Log + value; } remove { _Log = _Log - value; } }
public void AddMsg (int priority, string msg) { AddMessageEventHandler l = _Log; if (l != null) l (null, new LoggerEventArgs (priority, msg)); } }}
A Class directs output to the console
class ConsoleLogger{
static ConsoleLogger(){
Logger.Log += new AddMessageEventHandler(Logger_Log);}private static void Logger_Log(object sender, LoggerEventArgs msg){
Console.Error.WriteLine("{0}:\t{1}",msg.Priority.ToString(),msg.Message);
}}
A Class directs output to the system event log
class EventLogger{ private static string eventSource; private static EventLog logDest;
static EventLogger() { logger.Log +=new AddMessageEventHandler( Event_Log ); }
public static string EventSource { get { return eventSource; }
set { eventSource = value; if ( ! EventLog.SourceExists( eventSource ) ) EventLog.CreateEventSource( eventSource, "ApplicationEventLogger" );
if ( logDest != null ) logDest.Dispose( ); logDest = new EventLog( ); logDest.Source = eventSource; } }
private static void Event_Log( object sender, LoggerEventArgs msg ) { if ( logDest != null ) logDest.WriteEntry( msg.Message, EventLogEntryType.Information, msg.Priority ); }}
Event notification
• Events notify any number of interested clients that something happened
• The Logger class does not need any prior knowledge of which objects are interested in logging events
The extended Logger class (1/2)
public class Logger{ private static System.ComponentModel.EventHandlerList Handlers = new System.ComponentModel.EventHandlerList();
static public void AddLogger(string system, AddMessageEventHandler ev ) { Handlers[ system ] = ev; }
static public void RemoveLogger( string system ) { Handlers[ system ] = null; }
The extended Logger class (2/2) static public void AddMsg ( string system, int priority, string msg ) { if ( ( system != null ) && ( system.Length > 0 ) ) { AddMessageEventHandler l = Handlers[ system ] as AddMessageEventHandler;
LoggerEventArgs args = new LoggerEventArgs( priority, msg ); if ( l != null ) l ( null, args );
// The empty string means receive all messages: l = Handlers[ "" ] as AddMessageEventHandler; if ( l != null ) l( null, args ); } }}
Event HandlerList collection
• A class that contains a large number of events in its interface– consider using this collection of event handlers
Summary
• Define outgoing interfaces in classes with events: – Any number of clients can attach handlers to the events
and process them. – Those clients need not be known at compile time.
• Use Event HandlerList collection to manage a large number of events
• Item 23: Avoid Returning References to Internal Class Objects
Does read-only property always work?
• No!– It may be broken by reference type function return
Examplepublic class MyBusinessObject{ // Read Only property providing access to a // private data member: private DataSet _ds; public DataSet Data { get { return _ds; } }}// Access the dataset:DataSet ds = bizObj.Data;
// Not intended, but allowed:ds.Tables.Clear( ); // Deletes all data tables.
Strategies for protecting your internal data structures• value types• immutable types• Interfaces• wrappers
Value types
• Any changes to the copy retrieved by the clients of your class do not affect your object's internal state
Immutable types
• You can return strings, or any immutable type, safely knowing that no client of your class can modify the string.
Interfaces
• By exposing the functionality through interfaces– Exposing the IListsource interface pointer in the DataSet
• Minimize the possibility that your internal data changes
Wrappers (1/2)
{ // Read Only property providing access to a // private data member: private DataSet _ds; public DataView this[ string tableName ] { get { return _ds.DefaultViewManager. CreateDataView( _ds.Tables[ tableName ] ); } }}
// Access the dataset:DataView list = bizObj[ "customers" ];foreach ( DataRowView r in list ) Console.WriteLine( r[ "name" ] );
Wrappers (2/2)public class MyBusinessObject{ // Read Only property providing access to a // private data member: private DataSet _ds; public IList this[ string tableName ] { get { DataView view = _ds.DefaultViewManager.CreateDataView ( _ds.Tables[ tableName ] ); view.AllowNew = false; view.AllowDelete = false; view.AllowEdit = false; return view; } }}
// Access the dataset: IList dv = bizOjb[ "customers" ]; foreach ( DataRowView r in dv ) Console.WriteLine( r[ "name" ] );
Take care on return value type
• Use the IList interface with any collection. it's not specific to the DataSet.
• You should not simply return the DataView object which users could easily enable the editing and add/delete capability again
Summary
• Exposing reference types through your public interface allows users of your object to modify its internals
• Limit that access by exposing private internal data using interfaces, or wrapper objects