Generics in C # programming

Source: Internet
Author: User
Tags naming convention
One of the drawbacks of the. Net 1.1 release is that it does not provide support for generics. By using generics, we can greatly improve the reuse of code, but also get strong type of support, avoid implicit boxing, unpacking, to some extent, improve the performance of the application. This article will discuss generics for you in a systematic way, and we'll start with understanding generics.


1.1 Understanding Generics


Why does 1.1.1 have a generic type?


I think no matter what way we get into the computer programming industry, it is unavoidable to face the topic of data structure and algorithm. Because it is a basic discipline of computer science, the more often the bottom part, the higher the requirement for the time efficiency and space efficiency of the data structure or algorithm.


For example, when you call the sort () method on an instance of a collection type (such as ArrayList) to sort it. NET Framework applies a fast sorting algorithm at the bottom. The Quick Sort method name in the NET Framework is called quicksort (), which is found in the array type, which can be seen through the Reflector.exe tool.


We are not going to talk about whether this quicksort () is good, efficient or not, which deviates from our subject. But I want to ask you to think about a question: What would you do if you were to implement a sort algorithm? Well, we've narrowed the topic down a little bit, we're going to implement one of the simplest bubble sort (Bubble sort) algorithms, and if you don't have the experience of using generics, I guess you might not hesitate to write the following code because this is the standard implementation of the University tutorial:

public class sorthelper{public    void Bubblesort (int[] array) {        int length = array. Length;        for (int i = 0, I <= length-2; i++) {for            (int j = length-1; J >= 1; j--) {                //Swap two elements for                if (array[j ] < Array[j-1]) {                    int temp = array[j];                    ARRAY[J] = array[j-1];                    ARRAY[J-1] = temp;}}}}    


For those unfamiliar with bubble sorting, you can safely ignore the method body of the above code, which does not pose any obstacle to your understanding of generics, as long as you know what it does: rearrange the elements of an array in order from small to large. Let's take a little test of this program:

Class Program {    static void Main (string[] args) {        Sorthelper sorter = new Sorthelper ();        Int[] Array = {8, 1, 4, 7, 3};        Sorter. Bubblesort (array);        foreach (int i in array) {            Console.Write ("{0}", i);        }        Console.WriteLine ();        Console.readkey ();    }}

The output is:

1 3 4) 7 8

We found that it worked well and was delighted to think it was the best solution. Until very soon, we need to sort an array of type Byte, and our sorting algorithm can only accept an array of type int, although we know that they are fully compatible, because the byte type is a subset of the int type, but C # is a strongly typed language, We cannot pass in a byte array where an int array type is accepted. Well, it doesn't matter, now it seems the only way to do that is to copy the code over and change the signature of the method:

public class Sorthelper {public    void Bubblesort (int[] array) {        int length = array. Length;        for (int i = 0, I <= length-2; i++) {for            (int j = length-1; J >= 1; j--) {                //Swap two elements for                if (array[j ] < Array[j-1]) {                    int temp = array[j];                    ARRAY[J] = array[j-1];                    ARRAY[J-1] = temp;}}}}    public void Bubblesort (byte[] array) {int length = array. Length;        for (int i = 0, I <= length-2; i++) {for            (int j = length-1; J >= 1; j--) {                //Swap two elements for                if (ARRA Y[J] < array[j-1]) {                    int temp = array[j];                    ARRAY[J] = array[j-1];                    ARRAY[J-1] = temp;}}}}    


OK, we solved the problem once again, although we always feel a bit awkward, but this code has been able to work, according to the idea of agile software development, not too early to abstract and respond to changes, when the change first occurs, the fastest way to solve it, when the change occurs the second time, then the better architecture and design.


This is done to avoid over-design, because it is possible that the second change will never occur, and you spend a lot of time and energy creating a "perfect design" that will never be used.


It's like a proverb, "Fool me Once,shame on you. Fool me twice, shame on me. "It means" fool me once, you are bad, fool me two times, I am stupid ".


Good things are always hard to long, we soon need to sort an array of type char, we can of course follow the practice of byte type array, continue to use copy paste Dafa, and then modify the method signature.


But unfortunately, we don't want it to fool us two times because no one wants to prove stupid, so it's time to think of a better solution.


We carefully contrast these two methods, we will find that the implementation of the two methods is exactly the same, in addition to the signature of the method is different, there is no difference. If you have ever developed a Web site program, you will know that for some very large sites, in order to avoid overloading the server, usually in the form of static page generation, because the use of URL rewriting still consumes a lot of server resources, but after the generation of HTML static Web page, The server simply returns the files requested by the client, which can greatly reduce the load on the server.


In the implementation of static page generation on the Web, there is a common method, that is, template generation method, which is the concrete way: each time a static page is generated, the template is loaded with some special characters marked placeholders, and then we read the data from the database, using the read data to replace the placeholder in the template, Finally, the template is saved as a static HTML file on the server by a certain naming convention.


We find that the situation here is similar, and I'm going to make an analogy with it: we treat the method as a template, treat its method signature as a placeholder, because it's a placeholder, so it can represent any type, This and the static page generated when the template placeholder can be used to represent any data from the database The truth is the same. The next step is to define the placeholder, and we'll look at the signatures of these three methods:

public void Bubblesort (int[] array) public void Bubblesort (byte[] array) public void Bubblesort (char[] array)


You'll find that the best way to define placeholders is to replace int[], byte[], char[] with placeholders, and we'll take this placeholder with t[], where t can represent any type, which masks the difference between the three method signatures:

public void Bubblesort (t[] array) {    int length = array. Length;    for (int i = 0, I <= length-2; i++) {for        (int j = length-1; J >= 1; j--) {            //Swap two elements for            if (array[j ] < Array[j-1]) {                T temp = array[j];                ARRAY[J] = array[j-1];                ARRAY[J-1] = temp;}}}}    


It looks much fresher now, but we have another problem: when we define a class, and the class needs to refer to a type other than itself, we can define a constructor with parameters, and then pass the parameters it needs from the constructor. But on top of that, our parameter T itself is a type (similar to an int, byte, char, not an instance of a type, such as 1 and ' a ').


It is clear that we cannot pass an array of this type T in the constructor, because the arguments are in the position of the type instance, and T is the type itself, and it is in the wrong position. For example, the following are the usual constructors:


Public Sorthelper (Type type instance name);


The constructor functions we expect are:


Public sorthelper (type);


Instead of using a special syntax to pass this T-placeholder, let's define a syntax to pass it:

public class Sorthelper<t> {public    void Bubblesort (t[] array) {        //method implementing body    }}


We add an angle bracket to the name of the class and use this angle bracket to pass our placeholder, which is the type parameter. Next, let's look at how to use it when we need to sort an array of type int:

Sorthelper<int> sorter = new sorthelper<int> (); int[] array = {8, 1, 4, 7, 3};sorter. Bubblesort (array);


When we need to sort an array of type byte:

Sorthelper<byte> sorter = new sorthelper<byte> (); byte [] array = {8, 1, 4, 7, 3};sorter. Bubblesort (array);


I believe you have discovered that what you have done above has actually implemented a generic class. This is one of the most typical applications of generics, and we can see that by using generics, we greatly reduce the duplication of code, making our program more refreshing, and the generic class is similar to a template that can pass in any of the types we need for this template when needed.


We are now more professional, a formal name for the placeholder for this section, in. NET, which is called the type parameter (type Parameter), the following section, we will learn the type parameter constraints.


1.1.2 Type parameter constraints


In fact, if you run the code above, you'll find it doesn't even compile, why? Consider such a question, if we customize a type, it defines the book, the name is "books", it contains two fields: one is an int type ID, is the book identifier; one is a string type title, which represents the title of the book. Because we are an example here, in order to both illustrate the problem and not deviate from the topic, this book type only contains these two fields:

public class Book {    private int id;    private string title;    Public book () {} public book    (int ID, string title) {        this.id = ID;        this.title = title;    }    public int ID {        get {return Id;}        set {id = value;}    }    public string Title {        get {return Title;}        set {title = value;}}    }


Now, let's create an array of type book and try to sort it using the generic class defined in the previous section, and I think the code should look like this:

book[] Bookarray = new book[2]; Book Book1 = new book (124, ". NET Beauty "); Book Book2 = new Book ("C # 3.0 Revelation"); bookarray[0] = book1;bookarray[1] = Book2; Sorthelper<book> sorter = new sorthelper<book> (); Sorter. Bubblesort (Bookarray); foreach (book B in Bookarray) {    Console.WriteLine ("Id:{0}", b.id);    Console.WriteLine ("Title:{0}\n", B.title);}


Maybe now you still do not see what the problem, you think the previous section of the code is very general, then let us look more carefully, and then look at the Sorthelper class of the Bubblesort () method of implementation, in order to avoid you go back to the last section of the code, I copied it down:

public void Bubblesort (t[] array) {    int length = array. Length;    for (int i = 0, I <= length-2; i++) {for        (int j = length-1; J >= 1; j--) {            //Swap two elements for            if (array[j ] < Array[j-1]) {                T temp = array[j];                ARRAY[J] = array[j-1];                ARRAY[J-1] = temp;}}}}    


Although we are reluctant, but the problem still arises, since it is a sort, then it is inevitable to compare size, you can see in two elements to exchange the size of the comparison, then now ask: Book1 and book2 who is larger? Xiao Zhang may say Book1 big, because its ID is 124, and Book2 ID is 45, and Xiao Wang may say book2 big, because its title is "C" beginning, and Book1 title is "." The beginning of the (character sort when "." In front of "C"). But the program can not be judged, it does not know whether to follow the standard of the small Zhang or according to the standard comparison of Xiao Wang. At this point, we need to define a rule to compare.


In. NET, the basic way to achieve a comparison is to implement the IComparable interface, which has a generic version and a non-generic two version, because we are now explaining generics, in order to avoid "deadlock", so we adopt its non-generic version. It is defined as follows:

Public interface IComparable {    int CompareTo (object obj);}


If our book type has already implemented this interface, then when called to the following:

Book1.compareto (BOOK2);

If Book1 is smaller than Book2, returns an integer less than 0 if Book1 is equal to Book2, returns 0 if Book1 is larger than BOOK2, and returns an integer greater than 0.


Next let our book class to implement the IComparable interface, at this time we face the problem of sorting standards, said the popular point, is to use the standard of small Zhang or the standard of small Wang, here let us adopt the standard of small Zhang, with ID as standard to sort book, modify book Class, Let it implement the IComparable interface:

public class Book:icomparable {    //CODE: The above implementation is slightly public    int CompareTo (Object obj) {book        book2 = (book) obj;
  
   return This.Id.CompareTo (Book2. ID);}    }
  


To save space, I omitted the implementation of the book class above. It is also important to note that we did not compare the ID of the current book instance with the ID of the book instance passed in in the CompareTo () method, but instead delegated the comparison to the int type, because the int type also implements the IComparable interface. By the way, have you noticed that there is a problem with the code above?


Because this CompareTo () method is a very "generic" method, its parameters accept an object type parameter to ensure that all types can use the interface. Therefore, in order to get the book type, we need to make a downward cast in the method.


If you are familiar with object-oriented programming, then you should think that there is a violation of the Liskov substitution principle, I can not make a special talk about this principle, only to mention: This principle requires that the method should not be the method of the parameters accepted by the downward casting.


Why is it? We define the inheritance system for the purpose of common code, let the base class to achieve common responsibilities, and let the subclass implement its own responsibility, when you define a method to accept the base class, the design itself is good, but when you cast inside the method, it destroys the inheritance system, Because, although the signature of the method is interface-oriented programming, the method's internal or implementation-oriented programming.


Note: What is a "down cast (downcast)"? Because object is the base class for all types, the book class inherits from the object class, in which the object is on the upper level and book is on the lower level, so it is called a "down cast".


Well, let's get back to the point now, since we've got the book class to implement the IComparable interface, should our generic class work? No, because we have to remember: the generic class is a template class, it does not know about the type parameters passed at execution time, and does not make any guesses, we understand that the book class is now implemented IComparable, it is easy to compare it, but our sorthelper<t > Generic classes do not know, how to do? We need to tell the Sorthelper<t> class (exactly to tell the compiler) that the T-type parameter it accepts must be able to be compared, in other words, to implement the IComparable interface, which is the subject of this section: generic Constraints.


In order to require the type parameter T to implement the IComparable interface, we redefine the Sorthelper<t> as follows:

public class sorthelper<t> where t:icomparable {    //CODE: implementation slightly}

The above definition shows that the type parameter T must implement the Icomaprable interface, otherwise it will not compile, thus ensuring that the method body can run correctly. Since T has already implemented IComparable, and the members in array arrays are instances of T, when you click the decimal point after array[i]. , the VS200 smart hint will give the IComparable member, the CompareTo () method. Let's modify the Bubblesort () class so that it uses the CompareTo () method to compare:

public class sorthelper<t> where t:icomparable{public    void Bubblesort (t[] array) {        int length = array. Length;        for (int i = 0, I <= length-2; i++) {for            (int j = length-1; J >= 1; j--) {                                //Swap two elements for                if (array[j ]. CompareTo (Array[j-1]) < 0) {                    T temp = array[j];                    ARRAY[J] = array[j-1];                    ARRAY[J-1] = temp;}}}}    


Now that we run the code defined above again, we see the following output:


Id:45

Title:. The beauty of net


id:124

Title:c# 3.0 Revelation


In addition to being able to constrain the type parameter T to implement an interface, you can also constrain t to be a struct, T is a class, T has a constructor, T inherits from a base class, and so on, but I think it would be a waste of your time to list each of these usages.


So I'm not going to go into the discussion here, the concepts are exactly the same, but there are some differences in the syntax of the statement, and this difference, I believe you can easily resolve by looking at MSDN.


1.1.3 Generic methods


Let's consider the question: if we have a very complex class that performs a variety of scientific operations based on a certain field, we call this class Supercalculator, which is defined as follows:

public class Supercalculator {public    int superadd (int x, int y) {        return 0;    }    public int Superminus (int x, int y) {        return 0;    }    public string Supersearch (string key) {        return null;    }    public void SuperSort (int[] array) {    }}


Because this class has very high requirements for algorithms,. NET Framework built-in fast sorting algorithm does not meet the requirements, so we consider ourselves to implement a sorting algorithm, notice that the Supersearch () and SuperSort () methods accept different parameter types, so we'd better define a generic to solve, We call this algorithm speedsort (), since this algorithm is so efficient, we might as well define it as public, so that other types can be used, then according to the previous two sections of the knowledge learned, the code may resemble the following:

public class supercalculator<t> where t:icomparable {    //CODE: Slightly public    void Speedsort (t[] array) {              //C ODE: Implement slightly    }}


Here's a question about type design: Exactly, is it inappropriate to put the Speedsort () method in Supercaculator? Why is it? Because their duties are confused, supercaculator means "Super Calculator", then it contains the public methods should be related to the calculation, and Speedsort () appear here is not nondescript, when we find that the name of a method and the name of the class is not very important, You should consider abstracting this method and placing it in a new class, even if the class has only one method.


Here is just a demo, and we know that there is a problem. Well, let's get back to the point, though now the Supercalculator class can actually do what we need, but its use has become complicated, why? Because the Speedsort () method pollutes it, just to be able to use the Speedsort () method, we have to add the type parameter T to the Supercalculator class, so that even when the Speedsort () method is not called, You also have to accept a type parameter when creating an calculator instance.


To solve this problem, we naturally think that there is no way to add the type parameter T to the method, rather than the whole class, that is, to reduce the range of T action. The answer is yes, and this is the subject of this section: generic methods. Similarly, we just need to modify the signature of the Speedsort () method so that it accepts a type parameter, at which point the supercalculator is defined as follows:

public class supercalculator{    //CODE: Other implementations slightly public    void speedsort<t> (t[] array) where t:icomparable {
  
   //CODE: Implement slightly    }}
  


Next we write a piece of code to test it:

book[] Bookarray = new book[2]; Book Book1 = new book (124, "C # 3.0 Revelation"); Book Book2 = new book (45, ". NET Beauty "); Supercalculator calculator = new Supercalculator (); calculator. Speedsort<book> (Bookarray);


Because the Speedsort () method is not implemented, so this code does not have any output, if you want to see the output, you can simply paste the above bubble sort of code in, here I will no longer demonstrate. What I want to say here is an interesting compiler capability that infers the type of array you are passing and whether it satisfies the generic constraint, so the above Speedsort () method can also be called as follows:

Calculator. Speedsort (Bookarray);

This way, although it is a generic method, there is no difference between the use and the normal method.


1.1.4 Summary


In this section we learned the basics of mastering generics, and you see why you need generics, which avoids duplication of code and learns how to use type parameters and generic methods. With the knowledge of this section, you are able to cope with most of the scenarios in your daily development.

The above is the content of generics in C # programming, please pay attention to topic.alibabacloud.com (www.php.cn) for more relevant content.

  • 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.