Professional ASP.NET 1.1 [Electronic resources] نسخه متنی

اینجــــا یک کتابخانه دیجیتالی است

با بیش از 100000 منبع الکترونیکی رایگان به زبان فارسی ، عربی و انگلیسی

Professional ASP.NET 1.1 [Electronic resources] - نسخه متنی

Alex Homeret

| نمايش فراداده ، افزودن یک نقد و بررسی
افزودن به کتابخانه شخصی
ارسال به دوستان
جستجو در متن کتاب
بیشتر
تنظیمات قلم

فونت

اندازه قلم

+ - پیش فرض

حالت نمایش

روز نیمروز شب
جستجو در لغت نامه
بیشتر
توضیحات
افزودن یادداشت جدید


"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">








  • Strongly Typed Collections

    The fact that most of the collections classes in the .NET Framework class library are designed to work with the System.Object type makes them very versatile. However, this flexibility can also be viewed as a minor problem, since it means you have to do a lot of casting (assuming you know the type in the first place). For example, assuming a Hashtable contains a number of items of the type Product, you have to cast each item you retrieve. Using C#, you would write:


    Hashtable products;
    Product p;
    p = (Product) product["ProductCode"];

    Using VB.NET, you would write:


    Dim products As Hashtable
    Dim p As Product
    p = CType(products("ProductCode"), Product)

    Casting types like this isn't difficult, but it can make code less readable, and often leads to silly compiler errors when you forget to cast. Strongly typed collections can resolve these problems. Rather than directly using the collection classes such as Hashtable in your class, you should instead provide a custom type that encapsulates the collection type being used, and also removes the need for casting.

    For example, using C# you would write:


    ProductCollection products;
    Product p;
    p = products["ProductCode"];

    Using VB.NET, you would write:


    Dim products As ProductCollection
    Dim p As Product
    p = products("ProductCode")

    Implementing a strongly typed collection like this is relatively simple, and it allows you to build in additional rules and error handling, saving you from duplicating them throughout the code.

    To a strongly typed collection, you have to:



    • Define the custom type of the item held in the collection.



    • Create the collection class, and implement the Add, Remove, and GetEnumerator methods, as well as the Item property.



    Let's look at each of these in turn.


    Defining the Custom Type


    In the strongly typed example, a Product type was used. When implementing a collection, you should always have a custom type, and a collection for that custom type in which the collection name is simply the custom type name appended with Collection; for example, Product and ProductCollection, or Address and AddressCollection.

    Here is a class definition for a Product type, written using VB.NET:


    Public Class Product

    ' private fields
    Private _code As string
    Private _description As string
    Private _price As Double

    ' constructor
    Public Sub New(initialCode As String, _
    initialDescription As String, _
    initialPrice As Double)
    Code = initialCode
    Description = initialDescription
    Price = initialPrice
    End Sub

    Public Property Description As String
    Get
    Description = _description
    End Get
    Set
    _description = Value
    End Set
    End Property

    Public Property Code As String
    Get
    Code = _code
    End Get
    Set
    _code = Value
    End Set
    End Property

    Public Property Price As Double
    Get
    Price = _price
    End Get
    Set
    _price = Value
    End Set
    End Property

    End Class

    The Product type has three public properties:



    • Code: A unique code assigned to the product.



    • Description: A description of the product.



    • Price: The cost of the product.



    All of the properties have a get and set accessor, and their implementation simply stores or retrieves the property value in a private field. The field name is the same as the property name, but prefixed with an underscore. The Product type has a constructor that accepts three parameters, allowing quick initialization. For example:


    Dim p As Product
    p = New Product("PROASP3", "Professional ASP 3.0", 39.99)

    For the purposes of this example, let's define the classes within the ASP.NET page. Typically, you would define these in a separate compiled assembly. That topic was introduced in Chapters 3 and 4, and is covered in more detail in Chapter 17.


    Creating the Collection Class


    The ProductCollection class will support two key features:



    • Unordered enumeration of all the contained products.



    • Direct access of a product using a product code.



    Since the Hashtable class provides the collection functionality necessary for implementing these features, you can use a Hashtable internally within your collection class for holding items. Then, you can aggregate the functionality of Hashtable and expose it, to provide access to your items in a type safe way that doesn't require casting.

    Since an internal Hashtable is being used to hold the Product items, define a private field called _products of type Hashtable within the collection class. A new object instance is assigned to this field in the constructor:


    Dim _products as Hashtable
    Public Sub New()
    _products = New Hashtable()
    End Sub

    Implementing the Add Method


    The Add method allows a new Product to be added to the collection:


    Public Sub Add( Item as Product )
    If Item Is Nothing Then
    Throw New ArgumentException("Product cannot be null")
    End If
    _products.Add(Item.Code, Item)
    End Sub

    This method throws an ArgumentException if a null item parameter is passed. If the parameter is not null, the Code property of the passed Product is used as the key for the Product in the contained Hashtable. Depending on your requirements, you could perform additional business logic validation here and throw additional exceptions.

    Implementing the Remove Method


    The Remove method removes a Product from the collection. The implementation of the method simply calls the Remove method of the Hashtable:


    Public Sub Remove(Item as Product)
    _products.Remove(Item.Code)
    End Sub

    Implementing the Item Property


    The Item property allows a Product to be retrieved from, or added to the collection by specifying the product code:


    Public Default Property Item(Code as String) as Product
    Get
    Item = CType(_products(Code), Product)
    End Get
    Set
    Add(Value)
    End Set
    End Property

    The implementation of the Set accessor calls the Add method in order to add the new product to the internal Hashtable. The process is implemented in a way so that any business logic in the Add method (such as the Null check) is neither duplicated nor missed.

    Implementing the GetEnumerator Method


    To use your collection class with the for..each statements in VB.NET and C#, your collection class must have a method called GetEnumerator. Although not strictly necessary, the IEnumerable interface is also implemented using the VB.NET Implements keyword. This is good practice and requires very little work:


    Public Class ProductCollection
    Implements IEnumerable
    ' ...
    ' implement an enumerator for the products
    Public Function GetEnumerator() As IEnumerator
    Implements IEnumerable.GetEnumerator
    GetEnumerator = _products.Values.GetEnumerator()
    End Function
    ' ...
    End Class

    The GetEnumerator method has to return an enumerator object that implements the IEnumerator interface. You could implement this interface by creating another class, but since your collection class is using a Hashtable internally, it makes much more sense to reuse the enumerator object provided by that class when its values are enumerated. The Hashtable.Values property returns an ICollection interface and since the ICollection interface derives from IEnumerable, you can call GetEnumerator to create an enumerator object for the collection of values.


    Using the Collection Class


    With the Product and ProductCollection classes created, you can use them just like the other collections in this chapter, but this time with no casting. For example:


    ' Page-level variable
    Dim _products as ProductCollection = New ProductCollection

    Sub Page_Load(sender as Object, events As EventArgs)
    ' Runs when page is loaded

    Dim products As New ProductCollection()
    Dim p As product

    p = New Product("CAR", "A New Car", 19999.99)
    products.Add(p)

    p = New Product("HOUSE", "A New House", 299999.99)
    products.Add(p)

    p = New Product("BOOK", "A New Book", 49.99)
    products(p.Code) = p

    For Each p In products
    outResult1.InnerHtml &= p.Code & " - " & p.Description _
    & " - $" & p.Price & "<br />"
    Next

    products.Remove( products("HOUSE") )
    For Each p In products
    outResult2.InnerHtml &= p.Code & " - " & p.Description _
    & " - $" & p.Price & "<br />"
    Next

    outResult3.InnerHtml = "Description for code CAR is: " _
    & products("CAR").Description

    End Sub

    The complete code for this example is contained in the samples available for download, in both VB.NET and C#. The result of running this page is shown in Figure 15-16:


    Figure 15-16:


    The DictionaryBase and CollectionBase Classes


    The DictionaryBase and CollectionBase classes allow you to create a Hashtable or ArrayList collection that can validate, and therefore restrict, the types it contains. It's a simple process to create your own collection class by deriving from these classes.

    This simple ASP.NET page defines a MyStringCollection collection class, adds three strings and one integer, and then displays the contents:


    'our custom collection
    Class MyStringCollection
    Inherits CollectionBase
    ... implementation goes here...
    End Class

    Dim names As IList

    names = New MyStringCollection

    names.Add("Richard")
    names.Add("Alex")
    names.Add("Dave")

    Try
    names.Add(2002)
    Catch e As Exception
    outResult1.InnerHtml = "Error: " & e.Message
    End Try

    For Each name As String in names
    outResult2.InnerHtml &= name & "<br />"
    Next

    The Collection base class implements the IList and ICollection interfaces. All the members of these interfaces are defined explicitly, which is why in the sample code the names variables have been defined as type IList.

    Each of the collection base classes provides a number of virtual functions that are called when the collection is modified. For example, OnClear is called when a collection is cleared; OnInsert is called when an item is added; OnRemove when an item is deleted, and so on. By overriding one of these methods, you can perform additional checks and throw an exception if an undesired condition arises. For example, in the collection class, you could implement an OnInsert method that throws an ArgumentException if anything other than a string is added:

    Class MyStringCollection
    Inherits CollectionBase
    Overrides Protected Sub OnInsert(index as Integer, item as Object)
    If Not(TypeOf item Is String) Then
    Throw New ArgumentException("My collection only supports strings")
    End If
    End Sub
    End Class

    Figure 15-17 shows the results of running this code:


    Figure 15-17:

    The DictionaryBase class is used in the same way as the CollectionBase class and implements the IDictionary and ICollection interfaces.

    The ReadOnlyCollectionBase Class


    The ReadOnlyCollectionBase class provides functionality for exposing a read-only collection. The class implements the ICollection and IEnumerable interface. The items exposed are internally held in a protected ArrayList variable called InnerList. To use this class, you have to derive your own class from it, and populate the contents of the InnerList array.


    Disposable Enumerators


    When you enumerate a collection, the enumerator objects that implement the IEnumerator interface may require expensive resources. For example, depending on how underlying items are stored, a custom enumerator could be using a database connection, or be holding temporary files on disk. In these scenarios, it is important that the enumerable object releases resources it holds as soon as possible.

    Due to the non-deterministic way the CLR releases object references, any code you write that directly uses an IEnumerator interface must always check if the enumerator objects that provided the interface support the IDisposable interface. You must then call the Dispose method when you've finished with the enumerator. If you do not do this, the resources held by the enumerator may not be released for some time. When you use the for..each language statement in C# and VB.NET, this is done automatically .

    When you use the IEnumerator interface directly (or any other enumerable type), if you do not know whether an enumerator object supports the IDisposable interface, always check once you have finished with it. For example, in C#, you might write:


    IEnumerator e = c.GetEnumerator();
    try
    {
    while (e.MoveNext())
    {
    Foo x = e.Current;
    // ...
    }
    }
    finally
    {
    IDisposable d = e as IDisposable;
    if (d != null) d.Dispose();
    }

    If you know that an enumerator object supports IDisposable, you can call it directly:


    IEnumerator e = c.GetEnumerator();
    try
    {
    while (e.MoveNext())
    {
    Foo x = e.Current;
    // ...
    }
    }
    finally
    {
    ((IDisposable)e).Dispose();
    }

  • / 243