1. Trang chủ
  2. » Công Nghệ Thông Tin

Professional ASP.NET 1.0 Special Edition- P28 pps

40 120 0

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

THÔNG TIN TÀI LIỆU

Thông tin cơ bản

Định dạng
Số trang 40
Dung lượng 855,61 KB

Nội dung

{ BestMovieOfAllTime = "index too big"; } Since types such as the ArrayList will throw an ArgumentOutOfRangeException if an index is invalid, we declare an exception handler to catch an error and give the user some basic feedback. We should also handle the general exception case, since types could throw other unexpected exceptions: try { BestMovieOfAllTime = (string) movieList[6]; } catch( ArgumentOutOfRangeException rangeException ) { BestMovieOfAllTime = "index too big"; } catch( Exception e ) { // do something useful here } The ICollection Interface The ArrayList class supports three collection interfaces: IList, IEnumerable, and ICollection. We've covered the first two, now we'll turn our attention to ICollection. The ICollection interface defines methods and properties that allow us to:  Determine how many items are in a collection (using the Count property).  Copy the contents of a collection into a specified array at a given offset (using the CopyTo method).  Determine if the collection is synchronized and therefore thread-safe.  Determine the synchronization root object for the collection (using the SyncRoot property). The synchronization root object is the object that is locked and unlocked as collection operations are performed on a synchronized collection. The ICollection interface derives from the IEnumerable interface so it inherits the GetEnumerator method. The ICollection.Count Property The following code shows how we can use the ICollection.Count property to display the number of items in our movieList ArrayList: <p>There are <%=movieList.Count%> in the list.</p> For those types in the class library, the implementation of the Count property returns a cached field. It doesn't cause the size of a collection to be recalculated, so it isn't expensive to call, which means that we don't need to worry about caching the Count property in an effort to get efficiency. The ICollection.CopyTo Method The ICollection.CopyTo method allows the contents of a collection to be inserted into an array at a specified offset. If the array to which the contents are copied does not have sufficient capacity for the insertion an ArgumentException will be thrown. The following Visual Basic.NET code shows how we can copy the contents of the ArrayList into a string array (at the beginning) using the CopyTo method: Dim Animals As ArrayList = new ArrayList() Dim ArrayOfAnimals() As string Animals.Add("Cat") Animals.Add("Dog") Dim a As Array ArrayOfAnimals = Array.CreateInstance(GetType(String), 2) Animals.CopyTo(ArrayOfAnimals, 0) Dim Animal As String For Each Animal in ArrayOfAnimals Response.Write("<p>" & Animal) Next Using C# we would write: ArrayList Animals = new ArrayList(); string[] ArrayOfAnimals; Animals.Add("Cat"); Animals.Add("Dog"); ArrayOfAnimals = (string[]) Array.CreateInstance(typeof(string), 2); Animals.CopyTo(ArrayOfAnimals, 0); foreach( string Animal in ArrayOfAnimals ) { Response.Write("<p>" + Animal); } Here we create an ArrayList that contains a couple of animals, dynamically create a string array, and then use the CopyTo method of the ArrayList to populate the created array with the contents of the ArrayList. The ArrayList supports two additional overloads of CopyTo. The first overload doesn't require an index to be specified when the contents of the ArrayList are copied and inserted at the start of an array. The second overload allows a specified number of items, from a specified start position, to be copied from the ArrayList to a given index within another array. As the ArrayList class does not expose its internal array, we have to use either its AddRange or InsertRange method when we're copying data from an array into an ArrayList. The AddRange method accepts an ICollection interface and adds all items within the collection to the end of the array. In the following example we copy the contents of two arrays into an ArrayList. The code would result in the names being listed in this order: Tom, Mark, Paul, Jon. Using C# we would write: string[] Friends = {"Tom","Mark"}; string[] CoWorkers = {"Paul","Jon"}; ArrayList MergedList = new ArrayList(); MergedList.AddRange(Friends); MergedList.AddRange(CoWorkers); foreach (string Name in MergedList) { Response.Write("<p>" + Name ); } The InsertRange method accepts an ICollection interface as its second parameter, but expects the first parameter to be the index at which the new items are to be copied to. To make the names Paul and Jon appear at the front of the list, we could insert the second array using InsertRange with an index value of zero: string[] Friends = {"Tom","Mark"}; string[] CoWorkers = {"Paul","Jon"}; ArrayList MergedList = new ArrayList(); MergedList.AddRange(Friends); MergedList.InsertRange(0, CoWorkers); foreach( string Name in MergedList ) { Response.Write("<p>" + Name); } This code would result in the names being listed in this order: Paul, Jon, Tom, Mark. The ICollection.IsSynchronized Property ASP.NET enables web applications to share objects by using the Application and Cache intrinsic objects. If an object reference is shared this way, there is potential for multiple pages to be working simultaneously with the same object instance. If this happens, any changes to the object must be synchronized. If two pages try to modify an object at the same time without synchronization, there is a high probability that the object's state will become corrupted. Objects that can safely be manipulated simultaneously are thread-safe. A thread-safe object takes on the responsibility of guarding itself from concurrent access, providing any necessary synchronization. The ICollection.IsSynchronized property can be used to determine if an object is thread-safe. For performance reasons, most objects (including ArrayList) are not thread-safe by default. This means we should not share types like ArrayList in a web application without performing some form of synchronization. In classic ASP we could use the Application.Lock and Application.Unlock methods for synchronization and while they are available in ASP.NET, they're not suitable for this particular task. They provide a coarse-grained lock (which is application-wide) that simply isn't suitable for scalable applications. To safely share a collection object such as an ArrayList between multiple web pages, we need to use a synchronization helper object. Such a helper object provides the same public interface as a non-thread-safe type, but adds a synchronization wrapper around the methods and properties that would otherwise not be thread-safe. Since this is such a common requirement, the collection types (and many other framework classes) follow a common pattern. Types that need to work in a thread-safe way provide a public, static, method called Synchronized that takes a non-thread-safe object reference and creates and returns a thread-safe wrapper that can be used to synchronize calls. Both objects manipulate the same underlying state, so changes made by either the thread-safe or non-thread-safe object will be seen by any other code that holds a reference to either object. The following (Visual Basic.NET) code shows how a non-thread-safe ArrayList adds an item before creating a thread-safe wrapper: Dim NotThreadSafeArrayList As ArrayList NotThreadSafeArrayList = New ArrayList() NotThreadSafeArrayList.Add("Hello") Dim ThreadSafeArrayList As ArrayList ThreadSafeArrayList = ArrayList.Synchronized(NotThreadSafeArrayList) If ThreadSafeArrayList.IsSynchronized = True Then Response.Write("<p>It's synchronized") End If The following diagram illustrates how this ThreadSafeArrayList works by protecting calls, and then delegating the calls to the non-thread-safe object: The ICollection.SyncRoot Property When writing thread-safe code, it's common to have a handle or object that's used by all the possible code paths in different threads (web pages) in order to synchronize access to the shared state. The CLR automatically allows any .NET type instance to be a synchronization root ( SyncRoot) that can be locked and unlocked to ensure that one or more code statements are only ever executed by a single thread at a time. In Visual Basic.NET we can use an object reference in conjunction with the SyncLock statement to make code thread-safe: Sub DoSomethingWithAList( list as ArrayList ) SyncLock list list.Add("abc") End SyncLock End Sub In C# we can achieve the same result using the lock statement: void DoSomethingWithAList( ArrayList list ) { lock(list) { list.Add("abc"); } } Under the hood, both C# and Visual Basic.NET use the System.Threading.Monitor object to perform their synchronization. Both the C# and Visual Basic.NET compilers add exception handling around the statements being executed, to ensure that any exceptions that are not handled in the code do not result in synchronization locks not being released. Using SyncLock (or lock) we can achieve a fine-grained level of locking inside ASP.NET pages. Rather than having one global lock, which is effectively what Application.Lock and Application.Unlock provide, we can have many smaller locks, which reduces lock contention. The disadvantage to this approach is that it requires that page developers really understand multi-threading. Since the writing of multi-threaded applications is not simple (and something ASP.NET does its best to protect us from), using the Synchronized method to automatically make a type thread-safe is often the preferred approach to take. When acquiring or releasing a SyncLock we need to know what SyncRoot object to use. This is the function of the ICollection.SyncRoot property. Since we cannot assume any implementation knowledge about the type providing an interface, we cannot make any assumptions about which SyncRoot object to use either. Consider this Visual Basic.NET code: Dim NotThreadSafeArrayList As ArrayList NotThreadSafeArrayList = New ArrayList() NotThreadSafeArrayList.Add("Hello") Dim ThreadSafeArrayList1 As ArrayList Dim ThreadSafeArrayList2 As ArrayList Dim ThreadSafeArrayList3 As ArrayList ThreadSafeArrayList1 = ArrayList.Synchronized(NotThreadSafeArrayList) ThreadSafeArrayList2 = ArrayList.Synchronized(NotThreadSafeArrayList) ThreadSafeArrayList3 = ArrayList.Synchronized(NotThreadSafeArrayList) Here we're creating an ArrayList and then creating three synchronization wrapper objects, each of which exposes the ICollection interface. Since each of the wrapper objects actually manipulates the same underlying array, the SyncRoot object used by each of the wrappers is the non-thread-safe ArrayList: If we wanted to synchronize access to the array in a web page so that we could perform an atomic operation, such as adding the contents of the array to a database and clearing it, we'd need to lock out all other threads from using the ArrayList. If we only had a reference to an ArrayList object or an ICollection interface, and we didn't know whether or not that type was thread-safe, how could we achieve this? If we had a thread-safe wrapper, and wrote our code like this, it would fail miserably: SyncLock ThreadSafeArrayList1 ' Copy list to db and clear list (as an example) End SyncLock All this code does is acquire a lock on the thread-safe wrapper, not on the underlying list. Anybody using one of the other thread-safe wrappers would still be able to modify the list. The ICollection.SyncRoot property solves this problem. The implementation of this property should always return the appropriate SyncRoot object. We should therefore rewrite our code like this: SyncLock ThreadSafeArrayList1.SyncRoot ' Copy list to db and clear list (as an example) End SyncLock Working with Dictionary Objects With ASP 3 the state can be managed using the Session or Application intrinsic objects. These classes enable us to store and retrieve a variant value using a string key. With ASP.NET the same functionality is provided by the intrinsic objects, but the value type stored is now of type System.Object rather than variant. Since all types in .NET derive from System.Object, any built-in or custom type can be held in session or application state. The following code shows a simple example of storing and retrieving state using the Session intrinsic object. Using Visual Basic.NET we would write: Session("value1") = "Wrox" Session("value2") = "Press" Dim value As String value = CType(Session("value1"), string) [...]... operation: The System.Collections.Specialized Namespace The System.Collections.Specialized namespace contains collection classes that are suited to specialized tasks, as well as collection classes that are strongly typed, which, although it limits the types that can be contained, reduces the amount of casting needed The StringCollection Class The System.Collections.Specialized.StringCollection class... case-insensitive hash table is a common requirement, and so a helper class, System.Collections.Specialized.CollectionsUtil, is provided to assist For example: Hashtable names; names = CollectionsUtil.CreateCaseInsensitiveHashtable(); The System.Collections.Specialized namespace is imported by default into a ASP.NET page so we do not need to use fully qualified names or an import directive The Stack Class... Hashtable with the Intrinsic ASP.NET Objects There are a number of differences between using the Hashtable class and the ASP.NET intrinsic objects: The key values for a Hashtable are not restricted to strings Any type can be used, so long as the type used as a key overrides the System.Object.Equals and System.Object.GetHashCode methods String keys are case-sensitive whereas those of the ASP.NET intrinsic objects... are case-sensitive whereas those of the ASP.NET intrinsic objects are not When enumerating over an ASP.NET intrinsic object, we are enumerating over the keys associated with each contained item value The Hashtable returns both the key and value using the DictionaryEntry class When enumerating over an ASP.NET intrinsic object the Current property returns a System.String object (the key value) and not... concurrently from different threads: Dim s As Stack = New Stack() Dim s2 As Stack s2 = Stack.Synchronized(s) Unless we provide our own synchronization, we should always use a thread-safe wrapper if multiple ASP.NET pages are going to access a shared object using application state The Queue Class The Queue class provides a First-In First-Out (FIFO) collection This is useful when we need to process items in... which enables it to be called concurrently from different threads: Dim q As Queue = New Queue() Dim q2 As Queue q2 = Queue.Synchronized(q) Again, a thread-safe wrapper should always be used if multiple ASP.NET pages are going to access a shared object using application state The SortedList Class The SortedList class is an interesting collection class, as it's a cross between a Hashtable and an ArrayList... The IDictionary interface derives from the ICollection interface so it inherits all of its methods and properties, as well as those of the basic interface IEnumerable Case Sensitivity of Keys With the ASP.NET intrinsic objects, key values are case insensitive This means the following C# code, which uses the intrinsic Session object, will happily set and retrieve the value correctly, even though the... names.Add("Richard"); names.Add("Sam"); names.Add("Richard"); names.Add("Sam"); foreach (String name in names) { Response.Write("" + name); } %> The StringDictionary Class The System.Collections.Specialized.StringDictionary class provides an implementation of a hash table, in which the key and value are always of type System.String The key is also case-insensitive Internally, this class uses the . } Comparing Hashtable with the Intrinsic ASP. NET Objects There are a number of differences between using the Hashtable class and the ASP. NET intrinsic objects:  The key values for a. methods.  String keys are case-sensitive whereas those of the ASP. NET intrinsic objects are not.  When enumerating over an ASP. NET intrinsic object, we are enumerating over the keys associated. Basic .NET we would write: Session("value1") = "Wrox" Session("value2") = "Press" Dim value As String value = CType(Session("value1"),

Ngày đăng: 03/07/2014, 07:20

TỪ KHÓA LIÊN QUAN