Using C # To implement collection class overview. net collections and Related Technologies

Source: Internet
Author: User
Tags net command net command line
Overview: In a real object-oriented development project, we usually abstract common business entities into specific classes, such as employee, customer, and contact, most classes are associated or dependent on each other. For example, the relationship between the employee and the customer is generated through the contact, and the contact is dependent on the employee and customer. In the actual object application module, you may need to obtain a group of customer objects (that is, instances of the MERs collection class, such as customers ), point to one of the customer objects (such as MERS MERs [I]) and access the property 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 the customer, or even traverse the contacts object to find a contact abstract of the customer (that is, MERS MERs. [I]. contacts [X]. summary ). To meet the requirements of the above collection classes, compare. net Framework platform implementation, it is not difficult to find.. NET provides a series of classes that implement the set function in the collections namespace, and provides developers with flexible and diverse options based on the applicable environment: for example, accessing a wide range of arraylist and stringcollection through indexes; usually, the first-in-first-out queue and the second-in-first-out Stack are released after retrieval. The elements are accessed through the element key hashtable, sortedlist, listdictionary, and stringdictionary; nameobjectcollectionbase and namevaluecollection accessed by indexing or using element keys; and the features of collection classes are implemented in system. array class under array. This article will analyze and compare the differences between different implementation methods and the applicable environment through two typical ways of implementing a representative "collection class", so that you can understand and master the relevant technologies, I hope to serve as a reference for everyone's learning and development work (note: the debugging and running environment of the author is.. NET Framework SDK 1.1 ).
1. Implement the customers collection class by abstracting the base class inheritance from collectionbase:
First, you need to create a simple class customer that provides elements for the set:

/// <Summary>
/// Class that describes the basic information of a customer
/// </Summary>
Public Class Customer
{
/// <Summary>
/// CUSTOMER NAME
/// </Summary>
Public string name;

/// <Summary>
/// Set class that describes all customer contact information
/// </Summary>
// Public contacts = new contacts ();

/// <Summary>
/// Customer class constructor without Parameters
/// </Summary>
Public customer ()
{
System. Console. writeline ("initialize instance without parameter ");
}

/// <Summary>
/// Customer class constructor with Parameters
/// </Summary>
Public customer (string name)
{
Name = Name;
System. Console. writeline ("initialize instance with parameter ");
}
}

The above is a simple framework of the customer class. The practical customer class may have more fields, attributes, methods, and events. It is worth noting that the contacts collection class is also inline in the form of public fields in the customer class, and the customer can finally be formed. contacts [I] interface form, but this is not the most ideal collection class association method, temporarily comment it, and will be detailed for analysis later, the code of this class focuses on the framework of a simple class (relative to the conceptual category of the Collection class). In addition, the class constructor is overloaded, this parameter is optional when the instance of this class is declared with the name parameter or without parameters.
Next, let's look at our first collection class implementation, which is based on the collemers class derived from the collectionbase class:
/// <Summary>
/// MERs Mers is the collection class implementation of customer, inherited from collectionbase
/// </Summary>
Public class MERs: system. Collections. collectionbase
{
Public MERs ()
{

}
/// <Summary>
/// Self-implemented add Method
/// </Summary>
/// <Param name = "customer"> </param>
Public void add (customer)
{
List. Add (customer );
}
/// <Summary>
/// Self-implemented 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 );
}
}
}

Taking the MERs collection class as an example and combining the collection auxiliary technology, we hope you can understand the following knowledge:
Inherit collection classes from collectionbase
The MERs class inherits from collectionbase and does not need to declare a list object in the class as the container of the customer set, because the collectionbase class has a built-in list object, important ilist interfaces such as Count, clear, removeat, and so on have been implemented. (For details, refer to the collectionbase member in msdn.) You only need to display the add, remove, indexof, insert, and other interfaces, in the Code, only the full parameter version of the add and remove methods is implemented as an example. The implementation of this collection class is simple and efficient, and collectionbase has implemented more comprehensive functions. Implementers only need to expand the functions they need.

Simple implementation of Indexer
We are used to operating arrays in the form of array [I]. The collection class can be regarded as an "array of objects". In C #, The indexer is used to help the collection class implement the array index function:
Public customer this [int Index]
{
Get
{
Return (customer) list [Index];
}
}
After the preceding code is added to the MERs class, the customers read-only indexer that takes integer index as the parameter and uses list [Index] to forcibly convert the returned values of the customer type is implemented, the user uses MERs [I]. by name, you can access the name field of the I-th customer object in the MERs set. Is it amazing? The code of the indexer in this article does not take into account the issue of subscripts crossing the border. for cross-border processing methods, see remove methods like this. The author only implements get access to the indexer. The reason why set access is not implemented will be discussed below.

Two implementation methods of item
Those who have used VB must be familiar with MERs. itme (I ). name form, which achieves the same role as the indexer, that is, to access a specific object in the collection through an index value, but what form should item be implemented in C? The first implementation method should be attribute, but you will soon find that the C # Attribute does not support parameters, so you cannot pass the index value as a parameter, the compromise is implemented in the following way:
Public customer item (INT index)
{
Return (customer) list [Index];
}
This item method can work, but why is it a compromise, because the access to the item will use MERs. item (I ). the syntax format of name is different from that of C # '[]'. It is a little abrupt, but if you want to unify the syntax, is there a solution even if performance is affected? See the following code:
Public MERs item
{
Get
{
Return this;
}
}
This is an item interface implemented in the form of attributes. However, because the C # Attribute does not support parameters, we return the customers object itself, that is, when the item attribute of the MERs object is called, the call to the customers indexer is triggered. The performance is decreased, but the customers is implemented. item [I]. name syntax. Comparing the implementation of the two types of items, it is not difficult to draw a conclusion: the items implemented in the form of attributes without parameters depend on the class indexer. If this class does not implement the indexer, this attribute will not be available, and the performance of redirecting item access to the indexer will also decrease; the only reason is: Uniform C # index subscript access style; the benefits of using the method are exactly the opposite. In addition to the awkward syntax style, there is no dependency on the indexer and performance degradation problem. It is difficult for both fish and bear's paw to have both sides. How to choose between them should be determined based on the actual needs of development.
Application of the default and attribute for compilation in the intermediate language
If you have implemented a standard indexer and want to provide an interface named "item", the "class 'windowsapplication1" error will occur during compilation. MERs already includes the definition of "item", but you have not done anything except the index maker. What is the problem? We have. net intermediate language Il to find the answer, in.. Net command line environment or Visual Studio.. Net command prompt environment, enter ildasm to run. net Framework msil Disassembly tool. It can be compiled only when the indexer does not implement the item interface. net pe execution file, find the MERs class through the intuitive tree structure, you will accidentally find that the C # indexer is interpreted as an attribute named item, the following is the code of the indexer defined as the item attribute after Il decompilation:
. Property instance class windowsapplication1.customer
Item (int32)
{
. Get instance class windowsapplication1.customer windowsapplication1.mers MERs: get_item (int32)
} // End of property MERs: item
The problem finally comes to the fore, that is, the C # compiler interprets the indexer as an attribute named item, which is exactly the same as the item interface we expected to implement, therefore, the above compilation errors are inevitable. Can we tell the compiler not to name the indexer as the default item? The answer is yes.
The solution is to declare the features before the indexer implements:
[System. runtime. compilerservices. indexername ("item")]
Defining this indexername feature will inform the CSHARP compiler to compile the indexer into an item instead of the default item. The modified indexer il disassembly code is as follows:
. Property instance class windowsapplication1.customer
Item (int32)
{
. Get instance class windowsapplication1.customer windowsapplication1.mers MERs: get_item (int32)
} // End of property MERs: item
Of course, you can define the attribute names generated by the indexer as other names, not limited to items, as long as they are not reserved keywords of the Il language. After naming the indexer, You can freely add an interface named "item.

The following is the debugging code for the customer and MERs classes. In the MERs class of the author, at the same time, an indexer with the item as the special name, an items method, and an item attribute are created to implement three different access methods for the collection element. In actual project development, the indexing function of a class does not need to be repeated multiple times. It may be sufficient to add only the indexer or an indexer with a form of item:
Public class calltest
{
Public static void main ()
{
Customers custs = new customers ();
System. Console. writeline (custs. Count. tostring (); // test the Count attribute.

Customer acust = new customer (); // The constructor without parameters will be called
Acust. Name = "Peter ";
Custs. Add (acust); // test the add method.

System. Console. writeline (custs. Count. tostring ());
System. Console. writeline (custs. item [0]. Name); // obtain
Custs. Items (0). Name + = "Hu"; // call the items method to obtain
System. Console. writeline (custs [0]. Name); // call the indexer to obtain

Custs. Add (new customer ("Linnet"); // The constructor with the name parameter will be called
System. Console. writeline (custs. Count. tostring ());
System. Console. writeline (custs. Items (1). Name); // call the items method to obtain
Custs. item [1]. Name + = "Li"; // call the items method to obtain
System. Console. writeline (custs [1]. Name); // call the indexer to obtain

Custs. Remove (0); // test the Remove Method.
System. Console. writeline (custs. Count. tostring ());
System. Console. writeline (custs [0]. Name); // remove validity Verification
Custs [0]. Name = "test passed"; // call the indexer to obtain
System. Console. writeline (custs. item [0]. Name );
Custs. Clear ();
System. Console. writeline (custs. Count. tostring (); // clear validity Verification

}
}
Output result:
0
Initialize instance without Parameter
1
Peter
Peterhu
Initialize instance with Parameter
2
Linnet
Linnetli
1
Linnetli
Test passed
0

2. Use the built-in arraylist object to implement the collection class:
Perhaps experienced programmers have come up with the idea that they can create an array object in a class and encapsulate the access to this object in this class to implement a collection class. The following is the implementation framework of the contact element class and the contacts collection class using this idea:

Public class contact
{
Protected string summary;

/// <Summary>
/// Customer contact description
/// </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 contact ()
{

}
}

Public class contacts
{
Protected arraylist list;

Public void add (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 ();
}
}
Through the implementation of these two classes, we can summarize the following points:
Reasons for using arraylist
The arraylist class is used when contacts implements the built-in set object, instead of the array class that everyone is familiar with. The main reasons are: in the existing. in the. NET V1.1 environment, array has exposed ilist. add, ilist. insert, ilist. remove, ilist. removeat and other typical collection class interfaces. In fact, implementing these interfaces will always cause a notsupportedexception exception. It is not clear whether Microsoft will implement these interfaces in future versions, but the current version. net obviously does not support dynamic arrays. The recommended way to change the array size in MS is to copy the old array to the new array of the expected size and then delete the old array, this shows that it is time-consuming and labor-consuming, and cannot meet the requirement of adding and deleting elements to the Collection class at any time; arraylist has implemented key interfaces for collection classes such as ADD, clear, Count, indexof, insert, remove, and removeat, and supports read-only sets. In the ontacts class, the collection class is easily implemented by only a small amount of encapsulated code. Another question is, why do we not inherit from system. Collections. arraylist similar to customers to implement the collection class? The main reason is that the arraylist object is directly exposed to the class user, which leads to invalid value assignment. For example, the user calls arraylist. the add method is successfully executed no matter whether the input parameter type is contact or not. The class cannot control and check whether the type of the input object is the same as expected, this class only accepts the original intention of the contact type object, which also leaves a great security risk. In addition, if the contact object is obtained without forced type conversion, the contacts element cannot be used directly in the form of contact.
Set in the Collection class
During the implementation of the Collection class, whether using the index or the "item" attribute with the same functions as the index, it is inevitable that only getter is implemented to form a read-only index, getter and setter are also implemented to form a complete indexer access at the same time. In the preceding example, MERS MERs does not implement the setter of The indexer, forming a read-only indexer. However, in the debugging code of the customer and customers classes, the author uses the confusing "custs [0]. name = "test passed". In fact, the above statement will not go to the setter of the MERs indexer, but will first run the getter of the customers indexer to get a customer object, then set the name field of the customer (if the name element is an attribute, the setter of the customer class name attribute will be accessed ). So under what circumstances will the setter of the index be used? In fact, the setter of the Collection class makes sense only when the entire element class needs to be dynamically covered during runtime, such as "custs [I] = new customer () "assign a new customer object to an existing element of the custs collection class. Such an access form will allow the setter of MERs to be accessed, that is, the element object itself is re-allocated, instead of modifying some attributes of an existing object. That is to say, because the MERs class does not implement the setter of The indexer, the customers class does not provide a method to overwrite existing customers in the customer collection. In contrast, the contacts indexer provides both getter for the set element and setter for the set element, that is, the contacts class allows users to dynamically update the contact element. This problem can be clearly explained by running the following tests on the contacts and contact 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 output result is:
Indexer setter access
Indexer getter access
Setter access
Indexer getter access
Getter access
Mail contact about ticket
After clearly recognizing the role of the indexer setter, the class implementation should determine whether to establish a setter Mechanism for the indexer based on the actual business characteristics, access permission control, and security.
Attribute-powerful and flexible method of combining fields into one
In the initial implementation of the customer class, we used a public field name to access the customer's name information. Although it can work normally, we lack the ability to control the name field, no matter whether the user of the class uses valid and valid field value assignment, the value of the field will be modified, and there is no good mechanism, real-time synchronization (such as data storage and notification related elements) when values change. In addition, field initialization can only be completed in the class constructor, even if the name field is never accessed during the entire object lifecycle. Comparing the summary attribute implemented in the contact class, it is not difficult to find that the attribute has advantages: the attribute can be initialized at get, if the attribute involves the occupation of network, database, memory, thread, and other resources, the initialization delay will be optimized. After the attribute is encapsulated, the real customer contact description summary is well protected and can be verified before being assigned a value during set. Besides, before and after getter and setter, data access and other related operations can be performed, which is impossible with fields. Therefore, we can conclude that attributes are a more powerful and flexible alternative to environments where fields cannot meet requirements.
In addition, attributes integrate the "get" and "set" methods, and use a unified and natural interface name, compared with the Java object. getanything and object. the setanything syntax style is more friendly (in fact, the attributes in C # are only re-packaged for methods, and the anything attributes with getter and setter are in. net Il, it will still be decomposed into two methods: get_anything and set_anything called by the anything attribute ).
Inline methods of collection classes
In the original customer class, the public field public contacts = new contacts () was used to implement Customer. contacts [] is a collection class inline interface, which is the simplest but lacks security protection. As with the advantages of the preceding attributes, expose a public collection class interface in the form of attributes. when accessing the actual access, it is more appropriate to operate the encapsulated collection class, for example, you can change the interface declaration of the customer class inline set contacts:
Protected contacts cons; // used for real contacts objects encapsulated in the class
Public contacts // The contacts attribute exposed outside the class
{
Get
{
If (cons = NULL) cons = new contacts ();
Return cons;
}
Set
{
Cons = value;
}
}
In the end, the form of customers [I]. Contacts [X]. Summary is successfully implemented.
Best time for instantiation
The. NET type system is completely object-oriented, And all types are derived from system. Object. Based on their respective characteristics, they can be divided into two camps: Value Type and reference type. Value types include structures (simple numeric and Boolean types are also included) and enumeration. reference types include classes, arrays, delegates, interfaces, pointers, etc, one feature of objectization is that system resources are allocated to objects only when objects are instantiated. That is to say, objects are instantiated in a flexible and timely manner, which will have a positive impact on the optimal allocation of system resources. The "lazy initialization" proposed in some articles advocates Object Instantiation only when necessary. In line with this principle, from the external perspective of the class, class can be initialized when it is about to be used; in the class, elements such as attributes can also not be initialized in the constructor, it is not performed until the getter of the attribute is actually accessed. If the attribute has never been read, it is unnecessary to occupy resources such as the network, database, memory, and thread without meaning. However, Initialization is not as late as possible because initialization takes time. initialization before use may slow the response speed of the class and cannot meet users' real-time needs. Therefore, finding a balance between resource occupation and initialization time is the best time for instantiation.

Summary
This article focuses on two ways to implement the collection class-inheritance implementation from collectionbase and built-in arraylist object implementation. It shows you some collections, indexers, attributes, and features applications and. NET environment, such as class constructor, object optimization, class association and other related knowledge. Through simple examples and explanations in this article, we hope to inspire readers and introduce more incisive and reasonable basic theories and application models.

Related Article

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.