Implement collection classes through C # Overview. NET collections and related technologies

Source: Internet
Author: User
Tags abstract arrays implement include net command tostring valid visual studio
Collection Overview: In real object-oriented development projects, we usually abstract common business entities into specific classes, such as employee, Customer, contact, and so on, and most of the classes will have corresponding associations or dependencies between them, If employee and customer contact through contact, contact is dependent on employee and customer. In the Actual object application module, there may be a need to obtain a set of client objects (that is, instances of the Customers collection class, such as customers), pointing to one of the customer objects (such as customers[i]), By accessing the properties name (Customers[i] of this customer object. Name) and contacts (such as customers[i]. Contacts) to query the customer's name and contact records with that customer, or even traverse the Contacts object to find the customer's contact summary (that is, customers. [I].contacts[x]. Summary). In order to meet the requirements of the above set classes, it is not difficult to find out the platform implementation of the. NET Framework. NET provides a series of classes to implement collection functionality under the Collections namespace, and provides developers with flexible selectivity depending on the application environment: such as extensive ArrayList and StringCollection via index access ; a first-in, first-out, first out stack that is typically released after retrieval; access to its elements through element keys Hashtable, SortedList, ListDictionary, and StringDictionary NameObjectCollectionBase and NameValueCollection that are accessed by index or through element keys, as well as array classes that are implemented under System.Array with attributes of the collection class. This article will be through the realization of a representative "collection class" Two typical ways, analysis and comparison of different ways of implementation of differences and applicable environment, so that everyone understand and grasp the relevant technology, hope for everyone's learning and development work to play a role (note: The author's debugging run environment. NET Framework SDK 1.1).
1. Implements the Customers collection class in a way that inherits from the CollectionBase abstract base class:
First you need to create a simple class customer that provides elements for the collection:

<summary>
A class that describes a customer's basic information
</summary>
public class Customer
{
<summary>
Customer Name
</summary>
public string Name;

<summary>
Collection class that describes all customer contact information
</summary>
Public Contacts contacts=new Contacts ();

<summary>
Customer class constructors with no parameters
</summary>
Public Customer ()
{
System.Console.WriteLine ("Initialize instance without parameter");
}

<summary>
Customer class constructors with parameters
</summary>
Public Customer (string name)
{
Name=name;
System.Console.WriteLine ("Initialize instance with parameter");
}
}

The above is a simple framework for the customer class, which may have more fields, properties, methods, and events. It is noteworthy that in the customer class also in the form of public fields to achieve the Contacts collection class, the final form of the interface form Customer.contacts[i], but this is not the most ideal collection Class association, temporarily annotate it, later will be detailed analysis, The code for this class is a framework that illustrates a simple class (relative to the conceptual category of a collection Class), and it also overloads the class constructor to provide selectivity with either the name parameter or without parameters for declaring instances of the class.
Next look at our first collection class implementation, based on the customers class derived from the CollectionBase class:
<summary>
Customers is the customer's collection class implementation, inherited from CollectionBase
</summary>
public class Customers:System.Collections.CollectionBase
{
Public Customers ()
{

}
<summary>
Your own implementation of the Add method
</summary>
<param name= "Customer" ></param>
public void Add (Customer customer)
{
List.add (customer);
}
<summary>
Your own implementation of the Remove method
</summary>
<param name= "Index" ></param>
public void Remove (int index)
{
if (Index > Count-1 | | Index < 0)
{
System.Console.WriteLine ("Index not valid!");
}
Else
{
List.removeat (index);
}
}
}

Take the Customers collection class as an example, combined with the set of auxiliary technology, I hope you can understand the following knowledge:
Implementing collection classes from CollectionBase inheritance
The customers class inherits from CollectionBase and no longer needs to declare a list object within the class as a customer collection container because the CollectionBase class already has a list object built into it and has implemented count, Clear, RemoveAt, and so IList important interfaces (refer to the CollectionBase member in MSDN), only need to show the user to implement Add, Remove, IndexOf, insert and so on interface, The Add method and the integer parameter version of the Remove method are simply implemented as examples in the code. The realization of this kind of collection class has the simple and efficient characteristic, the collectionbase has already realized the more perfect function, the implementation person only expands the function which it needs on the foundation.

Simple implementation of indexers
We are used to manipulating arrays in the form of array[i], and the collection class can be considered an "array of objects," in C #, the indexer that helps the collection class implement the array indexing function:
Public Customer This[int Index]
{
Get
{
Return (Customer) List[index];
}
}
After adding the above code to the Customers class, we implement a customers class read-only indexer that takes the shape index as the parameter and List[index] The return value of the customer type after the type conversion, with the user being customers[i. Name, is it amazing to access the name field of the first customer object in the Customers collection? The indexer code in this article does not take into account the problem of scaling out of bounds, and the handling of the boundary should refer to a similar remove method. The author only implements the get access to the indexer, and the reason for not implementing set access is discussed in the following article.

Two ways to implement item
The friends who have used VB must be very familiar with customers.itme (i). The form of name, which implements the same function as an indexer, that is, to access a specific object in the collection body through an index value, but how should item be implemented in C #? The first thought of the implementation path should be attributes, but you will soon find that C # attributes do not support parameters, so you cannot pass the index value as a parameter, the compromise approach is to implement the method:
Public Customer Item (int Index)
{
Return (Customer) List[index];
}
This item method is already working, but why is it a compromise, because access to item will be Customers.item (i). The syntax of the name, and C # ' [] ' count group subscript style is not uniform, obvious some abrupt, but if you want to achieve unity in the grammar, even if the performance by some impact also does not matter if there is no solution to it? Please see the following code:
Public Customers Item
{
Get
{
return this;
}
}
This is the item interface implemented as an attribute, but because the properties of C # do not support parameters, we return the Customers object itself, which is a call to the customers indexer when the Customers object Item property is invoked, and performance is degraded. But it did achieve customers.item[i]. The syntax style of name is uniform. Comparing the two item implementations, it is not difficult to draw the conclusion that: an item implemented as an attribute with no parameters depends on the indexer of the class, which cannot be used if the class does not implement the indexer, and because the access to the item is redirected to the indexer performance; the only reason is that Unified C # indexed subscript access style; the benefit of the method implementation is just the opposite, in addition to the syntax style is more awkward, there is no dependency on the indexer, performance degradation problem. The fish and bear's paw is difficult to both have, how to choose and decide according to the actual demand of development.
The compiler default of intermediate language and the application of attribute
If you implement a standard indexer and want to provide an interface named "Item", there will be errors at compile time "class ' Windowsapplication1.customers ' already contains the definition of" item ", but you do nothing except to build an indexer. Where exactly is the problem? We have to go from. NET intermediate language Il to find the answer, in the. NET command-line environment or in the visual Studio. NET command prompt environment, enter ILDASM to run the. NET Framework MSIL disassembly tool, through the main menu of ' Open ' load can be compiled through the only indexer without the item interface implementation of the. NET PE executable file, through an intuitive tree structure diagram to find the Customers class, you will accidentally find that the C # indexer is interpreted as a property named item, The following is the IL-compiled indexer code that is defined as the Item property:
. property instance Class Windowsapplication1.customer
Item (Int32)
{
. Get instance Class Windowsapplication1.customer Windowsapplication1.customers::get_item (Int32)
}//End of property Customers::item
The problem finally came to the bottom, is the C # compiler ' smart-aleck ' to interpret the indexer as a property named item, and we expect to implement the item interface exactly the same name, so the compiler error is also inevitable. So, do we have a way to tell the compiler not to name the indexer the default item? The answer is yes.
The workaround is to declare the attribute before the indexer implementation:
[System.Runtime.CompilerServices.IndexerName ("item")]
Defining this IndexerName feature will tell the CSharp compiler to compile the indexer into item instead of the default item, and the modified indexer Il disassembly code is:
. property instance Class Windowsapplication1.customer
Item (Int32)
{
. Get instance Class Windowsapplication1.customer Windowsapplication1.customers::get_item (Int32)
}//End of property Customers::item
You can, of course, define the generated property name of the indexer as a different name than item, as long as it is not a reserved keyword in the Il language. After naming the indexer, you are free to add an interface named "Item" to implement it.

The following is the debug code for the customer class and the Customers class, in the author's customers class, to illustrate the problem, and to create three different ways to access the collection elements by using an indexer with the item attribute name, a items method, and an item property. In actual project development, the indexing function of a class does not need to be repeated multiple times, and it may suffice to implement only an indexer or an indexer plus a form of item:
public class Calltest
{
public static void Main ()
{
Customers custs=new Customers ();
System.Console.WriteLine (custs. Count.tostring ());//count Property Test

Customer Acust=new Customer ()//constructor with no arguments
Acust.name = "Peter";
Custs. ADD (acust);//add method Test

System.Console.WriteLine (custs. Count.tostring ());
System.Console.WriteLine (custs. Item[0]. Name);//Call Item property gets
Custs. Items (0). name+= "Hu";//Call the items method to get
System.Console.WriteLine (Custs[0]. Name);//Call Indexer Get

Custs. Add (New Customer ("Linnet"));//The constructor with the name parameter will be invoked
System.Console.WriteLine (custs. Count.tostring ());
System.Console.WriteLine (custs. Items (1). Name);//The call to the items method gets
Custs. ITEM[1]. name+= "Li";//Call the items method to get
System.Console.WriteLine (Custs[1]. Name);//Call Indexer Get

Custs. Remove (0);//remove method Test
System.Console.WriteLine (custs. Count.tostring ());
System.Console.WriteLine (Custs[0]. Name);//remove validation
Custs[0]. Name= "Test passed";//Call Indexer Get
System.Console.WriteLine (custs. Item[0]. Name);
Custs. Clear ();
System.Console.WriteLine (custs. Count.tostring ());//clear validation

}
}
The output results are:
0
Initialize instance without parameter
1
Peter
Peterhu
Initialize instance with parameter
2
Linnet
Linnetli
1
Linnetli
Test passed
0

2. Implement collection classes in the form of built-in ArrayList objects:
Perhaps experienced engineers have long thought that an array object can be built into a class, and in that class, by encapsulating access to the object, the collection class can be implemented. The following is the implementation framework for the contact element class and the Contacts collection class using this idea:

public class contacts
{
protected string Summary;

<summary>
Customer Contact Instructions
</summary>
public string Summary
{
Get
{
System.Console.WriteLine ("Getter Access");
Return summary;//do something, as get data from data source
}
Set
{
System.Console.WriteLine ("Setter Access");
summary=value;//do something, as check validity or Storage
}
}

Public Contacts ()
{

}
}

public class Contacts
{
protected ArrayList List;

public void Add (contacts contact)
{
List.add (contact);
}

public void Remove (int index)
{
if (Index > List.count-1 | | Index < 0)
{
System.Console.WriteLine ("Index not valid!");
}
Else
{
List.removeat (index);
}
}

public int Count
{
Get
{
return list.count;
}
}

Public Contact This[int Index]
{
Get
{
System.Console.WriteLine ("Indexer getter access");
return (contact) List[index];
}
Set
{
List[index]=value;
System.Console.WriteLine ("Indexer setter access");
}

}

Public Contacts ()
{
List=new ArrayList ();
}
}
With the implementation of these two classes, we can summarize the following points:
The reason for adopting ArrayList
When contacts implements a built-in collection object, the ArrayList class is used instead of the more familiar array class, the main reasons are: in the existing. NET v1.1 Environment, array has exposed the typical collection class interfaces such as Ilist.add, Ilist.insert, Ilist.remove, Ilist.removeat, and actually implementing these interfaces always raises NotSupportedException exception, Microsoft is not known in future releases, but the current version of the. NET does not yet support dynamic arrays, and the recommended change of array size in MS is to delete old arrays by copying them to a new array of expected dimensions, which shows that it is time-consuming and laborious to make a detour that does not satisfy the need for the collection class to add deletion elements at any time. ArrayList has implemented key interfaces for collection classes such as Add, clear, Count, IndexOf, Insert, Remove, RemoveAt, and has the ability to support read-only collections, in the upper contacts class, with very little encapsulation code, It is easy to implement the collection class. The other question is why don't we implement the collection class in a way that is similar to customers from System.Collections.ArrayList inheritance? This is mainly because exposing the ArrayList object directly to the consumer of the class will result in an illegal assignment, such as a user calling ArrayList. The Add method, regardless of whether the input parameter type is contact, the method will be executed successfully, the class can not control and check the type of the input object and the expectation of the same, it is contrary to this class only accept the contact type object's original intention, but also left a great security risks; Without forced type conversions, the contacts element cannot be used directly in the contact type form.
Set in the collection class
In the implementation of the collection class, whether using indexers or the "Item" property of the same functionality as the indexer, it is unavoidable to consider whether to implement only getter-only-read indexers, or both getter and setter to form complete indexer access. In the example class customers above, there is no setter for indexers to form a read-only indexer, but in the debug code of the customer class and the Customers class, the author uses an easily confusing "custs[0". Name= the Access form of "Test passed", in fact, the above sentence does not enter the setter of the customers indexer but executes the getter of the customers indexer to get a customer object, The customer's Name field is then set (if the name element is an attribute, the setter of the Customer Class Name property is accessed). So under what circumstances will the indexer's setter be used? In fact, the setter of the collection class becomes meaningful only if you need to dynamically overwrite the entire element class at run time, such as "custs [I]=new Customer ()" Assigns a new customer object to an already existing element of the Custs collection class, Such access forms will result in the customers setter being accessed, that is, the element object itself is reassigned, not just some properties of the existing object. That is, because the customers class does not have a setter to implement indexers, the customers class does not provide a way to "overwrite" customers in the customer collection. In contrast, an indexer of the contacts class provides both getter for the element of the collection and a setter for the element of the collection, that is, the contacts class allows the user to dynamically update the contact element. This problem is clearly illustrated by running the following tests on the contacts and contact two classes:
public class Calltest
{
public static void Main ()
{
Contacts cons=new Contacts ();
Cons. ADD (new Contact ());
Cons[0]=new contact ();//trigger Indexer Setter
Cons[0]. summary= "Mail contact about ticket";
System.Console.WriteLine (Cons[0]. Summary);
}
}
The resulting output is:
Indexer Setter Access
Indexer Getter Access
Setter Access
Indexer Getter Access
Getter Access
Mail contact about ticket
After recognizing the role of indexer setter, it is necessary to make a setter mechanism for the indexer in the realization of the class by synthesizing the actual business features, access control and security.
Properties-a powerful and flexible field method for merging
When the customer class is initially implemented, we used a public field name to access the customer's name information, and while it worked, we lacked control over the name field, and the value of the field was modified regardless of whether the class's consumer used a legally valid field assignment And there is no good mechanism for real-time synchronization (such as data storage, notification of related elements, etc.) in the event of a value change, and the initialization of the field can only be done in the constructor of the class, even though the name field has never been accessed during the entire object lifecycle. Compared with the summary attribute we implement in the contact class, it is not difficult to discover the advantages of attributes: Properties can be initialized at get time, if the attribute involves the way of resource usage such as network, database, memory and thread, postpone initialization time, it will play a certain role in optimization. , through the encapsulation of the property, the real customer contact Description Summary is well protected, in the set, can be validated and then the assignment operation, and in the getter and setter before and after the data access and other related operations, this point with the field is impossible to achieve. So we can conclude that attributes are a more powerful and flexible alternative in environments where the field does not meet the requirements.
In addition, the attribute consolidates the "get" and "set" two "methods", and adopts the uniform natural Interface name, which is more affinity than the object.getanything and object.setanything syntax styles in the Java language (in fact, C # The property in is simply a wrapper over the method, with the getter and setter anything properties in. NET IL, it will still be decomposed into a get_anything and set_anything two methods called by the anything property.
How collection classes are inline
Public fields are used in the original customer class in the article Contacts=new Contacts () Contacts () implements the customer. Contacts[] form of the collection class inline interface, this is one of the most simple but lack of security protection of the collection class integration approach, as described above some of the advantages of attributes, the use of attribute form to expose a common collection class interface, in the actual access to access, It is a more appropriate solution to operate on a collection class that is sealed, such as contacts the interface declaration of the collection that is inline with the customer class to:
protected Contacts cons; True Contacts object for encapsulation within a class
Public Contacts contacts//exposed to Contacts properties outside the class
{
Get
{
if (cons = null) cons=new Contacts ();
return cons;
}
Set
{
Cons=value;
}
}
In the end, Customers[i]. CONTACTS[X]. The form of summary was successfully realized.
The best time to instantiate
. NET type system is completely object-oriented, all types are derived from System.Object, depending on the characteristics of the type, can be divided into value types and reference types of two camps. Value types include structs (simple numeric and Boolean also included) and enumerations, reference types include classes, arrays, delegates, interfaces, pointers, and so on, one of the characteristics of the object is that the object is not allocated system resources until the object is instantiated, that is, the object is flexibly and appropriately instantiated, The optimal allocation of system resources will have a positive significance. In some articles, the proposed "Lazy initialization" advocates the instantiation of objects when necessary, in this principle, from the outside of the class, a class can be initialized when it is about to be used, or an element within a class, such as a property, can be initialized in a constructor, It does not have to be meaningless to consume resources such as networks, databases, memory, and threads until the property's getter is actually accessed and if the attribute has not been read. However, it is not initialized as late as possible, because initialization takes time, initialization before use may cause the class to respond too slowly to meet the user's real-time needs. So the best time to instantiate is to find a balance between resource occupancy and initialization.

Summarize
This paper focuses on two ways to realize the collection class-from the CollectionBase inheritance realization and the built-in ArrayList object realization, has shown the partial collection, the indexer, the attribute, the characteristic application as well. NET environment, such as class constructors, object optimizations, class associations, and other related knowledge. Through the simple example and elaboration of this article, hope can inspire the reader's inspiration, introduce more incisive and reasonable basic theory and application model.



Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.