Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống
1
/ 82 trang
THÔNG TIN TÀI LIỆU
Thông tin cơ bản
Định dạng
Số trang
82
Dung lượng
489,71 KB
Nội dung
60 Chapter 2 • Introducing C# Programming The set accessor assigns a value to the member variable m_SSN.The value keyword contains the value of the right side of the equal sign when invoking the set property.The data type of value will be the type in the declaration of the property. In this case, it is a string. One thing to note about the set accessor is that it can do more than just set the value of a data member. For instance, you could add code to validate the value and not do the assignment if validation fails. NOTE Throughout the samples in this chapter, you will see a lot of string oper- ations that use the overloaded concatenation operators such as “+” and “+=” as in the following code: string string1 = "a" + "b" + "c"; string1 += "e" + "f"; In C#, strings are immutable, which means they cannot be changed once they have a value assigned to them. In the previous example, each time the string is modified, a new copy of the string is created. This can lead to performance problems in code that does a large amount of string operations. The .NET Framework supplies the System.Text.StringBuilder class, which allows you to create and manipu- late a string using a single buffer in memory for cases where you do a lot of string processing. Accessing Lists with Indexers The need to create and manipulate lists is a common programming task. Let’s extend our employee example from the last section. Let’s say you need to display a list of employees.The most logical thing to do would be to create a new Employees class, which contains all of the individual Employee instances.You would then iterate through all of the employees displaying each one until there are no further employees. One way to solve this would be to create a property that returns the number of employees and a method that returns a given employee given its position in the list, such as the following: for ( i = 0; i < employees.Length; i++ ) { www.syngress.com Introducing C# Programming • Chapter 2 61 Employee employee = employees.getEmployee( i ); Console.WriteLine( employee.LastName ); } However, it would be more intuitive if we could just treat the list of employees as an array contained with the Employee object. Here is what that might look like: for ( i = 0; i < employees.Length; i++ ) { Console.WriteLine( employees.[i].LastName ); } This is precisely what indexers do.They let you use array syntax to access a list of objects contained inside another class. Indexers do not imply a specific implementation for the list, however.The list within the containing class could be a static array, a file on disk, one of the collection classes supplied by the .NET Framework, or some other implementation. If the underlying implementation is changed from a static array to a collection class, for example, a programmer using the Employees class would not need to change her code.This is highly desirable and analogous to the same situation described in the section discussing properties in this chapter. Figure 2.6 extends the code listing in Figure 2.5 to make use of an indexer to iterate through a list of employees.The program is included on the CD in the file Indexers.cs. Figure 2.6 The Indexers.cs Program Listing using System; using System.Collections; /// <summary> /// Contains the program entry point for the Indexers Sample. /// </summary> class IndexersSample { static void Main( string[] args ) { try { www.syngress.com Continued 62 Chapter 2 • Introducing C# Programming // Create a container to hold employees Employees employees = new Employees(4); // Add some employees employees[0] = new Employee ( "Timothy", "Arthur", "Tucker", "555-55-5555" ); employees[1] = new Employee ( "Sally", "Bess", "Jones", "666-66-6666" ); employees[2] = new Employee ( "Jeff", "Michael", "Simms", "777-77-7777" ); employees[3] = new Employee ( "Janice", "Anne", "Best", "888-88-8888" ); // Display the employee list on screen for ( int i = 0; i < employees.Length; i++ ) { string name = employees[i].FirstName + " " + employees[i].MiddleName + " " + employees[i].LastName; string ssn = employees[i].SSN; Console.WriteLine( "Name: {0}, SSN: {1}", name, ssn ); } Employee employee = employees["777-77-7777"]; if ( employee != null ) { string name = employee.FirstName + " " + employee.MiddleName + " " + employee.LastName; www.syngress.com Figure 2.6 Continued Continued Introducing C# Programming • Chapter 2 63 string ssn = employee.SSN; Console.WriteLine( "Found by SSN, Name: {0}, SSN: {1}", name, ssn ); } else { Console.WriteLine( "Could not find employee with SSN: 777-77-7777" ); } } catch ( Exception exception ) { // Display any errors on screen Console.WriteLine( exception.Message ); } } } /// <summary> /// Container class for employees. This class implements two /// indexers /// </summary> class Employees { private ArrayList m_Employees; private int m_MaxEmployees; public Employees( int MaxEmployees ) { m_MaxEmployees = MaxEmployees; m_Employees = new ArrayList( MaxEmployees ); www.syngress.com Figure 2.6 Continued Continued 64 Chapter 2 • Introducing C# Programming } // Here is the implementation of the indexer by array index public Employee this[int index] { get { // Check for out of bounds condition if ( index < 0 || index > m_Employees.Count - 1 ) return null; // Return employee based on index passed in return (Employee) m_Employees[index]; } set { // Check for out of bounds condition if ( index < 0 || index > m_MaxEmployees-1 ) return; // Add new employee m_Employees.Insert( index, value ); } } // Here is the implementation of the indexer by SSN public Employee this[string SSN] { get { Employee empReturned = null; www.syngress.com Figure 2.6 Continued Continued Introducing C# Programming • Chapter 2 65 foreach ( Employee employee in m_Employees ) { // Return employee based on index passed in if ( employee.SSN == SSN ) { empReturned = employee; break; } } return empReturned; } } // Return the total number of employees. public int Length { get { return m_Employees.Count; } } } /// <summary> /// Represents a single employee /// </summary> class Employee { private string m_firstName; private string m_middleName; private string m_lastName; private string m_SSN; www.syngress.com Figure 2.6 Continued Continued 66 Chapter 2 • Introducing C# Programming // Constructor public Employee( string FirstName, string LastName, string MiddleName, string SSN ) { m_firstName = FirstName; m_middleName = MiddleName; m_lastName = LastName; m_SSN = SSN; } // FirstName property public string FirstName { get { return m_firstName; } set { m_firstName = value; } } // MiddleName property public string MiddleName { get { return m_middleName; } set { m_middleName = value; } } // LastName property public string LastName { get { return m_lastName; } set { m_lastName = value; } } // SSN property www.syngress.com Figure 2.6 Continued Continued Introducing C# Programming • Chapter 2 67 public string SSN { get { return m_SSN; } set { m_SSN = value; } } } You can see how this sets the value of an item in the list and get the value of an item in the list using arraylike syntax such as this: employees[0] = new Employee ( "Timothy", "Arthur", "Tucker", "555-55-5555" ); string ssn = employees[i].SSN; The portion of the code that implements an Indexer follows: public Employee this[int index] { get { if ( index < 0 || index > 4 ) return null; return m_Employees[index]; } set { if ( index < 0 || index > 4 ) return; m_Employees[index] = value; updateCount(); } } www.syngress.com Figure 2.6 Continued 68 Chapter 2 • Introducing C# Programming This sample code implements two indexers, one based on an index entry in the list and the second based on the SSN of an employee.The code to imple- ment an indexer is just a property on the containing class.The only real differ- ence is that now the property takes the index within the list as a parameter.This example uses an ArrayList, which is part of the System.Collections namespace of the .NET Framework. So, the code to get an item in the list via an index entry just returns the item in the ArrayList based on the index entry requested. Similarly, the code to set an item in the list just sets the item in the ArrayList.A check is also done to validate that the index entry passed in is within bounds based on the maximum size of the list passed to the constructor of the Employees class. Our implementation is relatively simple in that it returns if the index is out of bounds. A better implementation would be to throw an exception.We cover exceptions later in this chapter. The code also implements a second read-only indexer based on SSN.This illustrates that an indexer can be implemented using more than just the index of an entry in the list. In the Main method of the program, you can see the fol- lowing statement: Employee employee = employees["777-77-7777"]; This code calls our SSN indexer implementation.The SSN indexer loops through the Employee instances contained in the m_Employees ArrayList. If it finds an Employee instance that has the SSN requested, it returns that Employee instance. If it doesn’t find it, it returns null. In C#, the foreach keyword is used to iterate through a list of objects con- tained within another object. Here is what our sample program would look like using foreach: foreach ( Employee employee in employees ) { string name = employee.FirstName + " " + employee.MiddleName + " " + employee.LastName; string ssn = employee.SSN; Console.WriteLine( "Name: {0}, SSN: {1}", name, ssn ); } To use the foreach keyword, the class that contains the list must implement the IEnumerable interface contained within the System.Collections namespace.The www.syngress.com Introducing C# Programming • Chapter 2 69 IEnumerable interface has one responsibility: return an instance of an object that implements the IEnumerator interface also from the System.Collections namespace. The class that implements the IEnumerator interface is responsible for main- taining the current position in the list and knowing when the end of the list has been reached.Although this seems overly complex, it allows the flexibility of having the implementation of IEnumerator be in the class containing the list or in a separate class. The complete sample that implements the IEnumerable interface is on the CD in the Enumerable.cs file. Because the ArrayList class already implements the IEnumerable interface, all that is necessary in the Employees class is to declare the class as implementing the IEnumerable interface and then provide the implementa- tion of the GetEnumerator method of the IEnumerable interface.The GetEnumerator method simply returns the ArrayList implementation.The relevant code from the sample on the CD that accomplishes this is shown here: /// <summary> /// Container class for employees. This class implements /// IEnumerable allowing use of foreach sytax /// </summary> class Employees : IEnumerator { // IEnumerable implementation, delegates IEnumerator to // the ArrayList public IEnumerator GetEnumerator() { return m_Employees.GetEnumerator(); } } At first glance, indexers seem somewhat complex, and talking about them in the abstract can be a bit confusing. However, when you see the code, it is rela- tively simple and provides a clean and simple syntax to iterate though a list of objects. Using Delegates and Events If you are familiar with Windows programming, you’ve most likely dealt with callbacks. Callbacks are method calls that are executed when some event happens www.syngress.com [...]... 82 Chapter 2 • Introducing C# Programming Figure 2. 9 Continued OnAddEmployee += new AddEmployeeEvent( m_logger.logAddRequest ); } // Drain the queue public void start() { if ( m_employees == null ) return; for ( int i = 0; i < m_lengthQueue; i++ ) { // Pop an add employee request off the queue string FirstName = m_msgQueue[i,0]; string MiddleName = m_msgQueue[i,1]; string LastName = m_msgQueue[i ,2] ;... queue public void start() { if ( m_employees == null ) return; Continued www.syngress.com Introducing C# Programming • Chapter 2 Figure 2. 7 Continued for ( int i = 0; i < m_lengthQueue; i++ ) { string FirstName = m_msgQueue[i,0]; string MiddleName = m_msgQueue[i,1]; string LastName = m_msgQueue[i ,2] ; string SSN = m_msgQueue[i,3]; // Invoke the callback registered with the delegate Console.WriteLine(... m_msgQueue[i,1]; string LastName = m_msgQueue[i ,2] ; string SSN = m_msgQueue[i,3]; Console.WriteLine( "Invoking delegate" ); // Invoke the delegate passing the data associated with // adding a new employee resulting in the subscribed // callbacks methods being executed, namely // Employees.this.addEmployee() Continued www.syngress.com Introducing C# Programming • Chapter 2 Figure 2. 8 Continued // and EmployeeQueueLogger.logAddRequest()... passing the object derived from EventArgs Changes to the multicast delegate code sample that implement events are shown in Figure 2. 9.The full source code for this sample is on the CD in the file Events.cs www.syngress.com 79 80 Chapter 2 • Introducing C# Programming Figure 2. 9 Relevant Portions of the Events.cs Program Listing /// /// Defines the data that will be passed from the event delegate... Console.WriteLine( exception.Message ); } } } /// /// Simulates our message queue /// class EmployeeQueueMonitor { // Delegate signature Continued www.syngress.com 71 72 Chapter 2 • Introducing C# Programming Figure 2. 7 Continued public delegate void AddEventCallback( string FirstName, string LastName, string MiddleName, string SSN ); // Instance of the delegate private AddEventCallback m_addEventCallback;... indicate errors that occur.To illustrate exceptions, consider the code snippet in Figure 2. 10 that builds upon the Enumerable sample from the Indexers section of this chapter.The complete sample is included on the CD in the file Exceptions.cs www.syngress.com 85 86 Chapter 2 • Introducing C# Programming Figure 2. 10 Relevant Portions of the Exceptions.cs Program Listing using System; using System.Collections;... 2 • Introducing C# Programming during processing For instance, a callback can be established to handle the processing of an incoming message on a communications port Another part of the communications program can wait for messages on a communications port and invoke the callback whenever a new message arrives Function pointers perform the same sort of tasks in straight C/C++ programs Delegates in C#. .. string m_fileName; public EmployeeQueueLogger( string fileName ) { m_fileName = fileName; } // Called by delegate whenever a new add employee message Continued www.syngress.com 77 78 Chapter 2 • Introducing C# Programming Figure 2. 8 Continued // appears in the message queue Notice the signature matches // that requried by AddEventCallback public void logAddRequest( string FirstName, string LastName, string MiddleName,... container to hold employees Employees employees = new Employees(4); // Create and drain our simulated message queue EmployeeQueueMonitor monitor = Continued www.syngress.com Introducing C# Programming • Chapter 2 Figure 2. 7 Continued new EmployeeQueueMonitor( employees ); monitor.start(); monitor.stop(); // Display the employee list on screen Console.WriteLine( "List of employees added via delegate:"... Simulates monitoring a message queue When a message appears /// the event is raised and methods subscribed to the event // are invoked /// Continued www.syngress.com Introducing C# Programming • Chapter 2 Figure 2. 9 Continued class EmployeeQueueMonitor { // Event signature for AddEmployeeEvent public delegate void AddEmployeeEvent( object sender, AddEmployeEventArgs e ); // Instance of the AddEmployeeEvent . </summary> class EmployeeQueueMonitor { // Delegate signature www.syngress.com Figure 2. 7 Continued Continued 72 Chapter 2 • Introducing C# Programming public delegate void AddEventCallback( string FirstName, string. IndexersSample { static void Main( string[] args ) { try { www.syngress.com Continued 62 Chapter 2 • Introducing C# Programming // Create a container to hold employees Employees employees = new Employees(4); //. + employee.MiddleName + " " + employee.LastName; www.syngress.com Figure 2. 6 Continued Continued Introducing C# Programming • Chapter 2 63 string ssn = employee.SSN; Console.WriteLine( "Found by