C # Sharp experience of the eight-spoke indexer and operator overloading

Source: Internet
Author: User
Tags abstract array bool implement inheritance interface variable access
Index

Indexing device

An indexer (Indexer) is a new class member introduced by C # that makes an object as convenient and intuitive as an array. Indexers are very similar to the properties we talked about earlier, but indexers can have argument lists and can only function on instance objects, not directly on the class. Here is a typical indexer design, where we overlook the specific implementation.

Class Myclass{public object here [int index] {get {//Fetch data} set { Save Data}}}
Indexers do not have names like properties and methods, which clearly express the characteristics of the indexer reference object. As with attributes, the value keyword has parameter-passing meaning in the statement block after set. In fact, from the compiled Il Intermediate Language code, the above indexer is implemented as:

Class Myclass{public Object get_item (int index) {//Fetch data} public void Set_item (int index, object va Lue) {//Save data}}
Since our indexer is compiled behind the get_item (int index) and set_item (int index, object value) Two methods, we cannot even declare the implementation of the two methods in the class that declares the indexer, and the compiler will complain about the behavior. This implied implementation of the method can also be called by us, inheritance and other operations, and our own implementation of the method identical. Familiarity with the low-level compiler implementation of the C # language provides a good basis for understanding the behavior of the C # indexer below.

As with methods, indexers have 5 access protection levels, and 4 inheritance behavior modifiers, as well as external indexers. There is no difference between these acts and the method, and there is no more to repeat. The only difference is that indexers cannot be static (static), which is easy to understand in the semantics of object references. It is worth noting that when overriding (override) implements indexers, you should use Base[e to access the indexer of the parent class.

The data type of the indexer, like the implementation of the property, is both the return type of the Get statement block and the type of the value keyword in the SET statement block.

The parameter list of indexers is also noteworthy. The characteristics of the index make it necessary for the indexer to have at least one parameter, which is in brackets after the This keyword. The parameters of an indexer can only be of a passing value type, and there can be no ref (reference) and out (output) adornments. The data type of the parameter can be any data type in C #. C # According to different parameters of the signature of the indexer polymorphism discrimination. All the parameters in the brackets are referenced under get and set, and the value keyword can only be passed as a parameter under set.

The following is a specific example of an indexer, which is helpful in understanding the design and application of indexers.

Using System;class bitarray{int[] bits;int length;public BitArray (int length) {if (length < 0) throw new Argumentexc Eption (); bits = new int[((length-1) >> 5) + 1];this.length = length;} public int Length {get {return Length;}} public bool This[int Index] {get {if (Index < 0 | | | Index >= length) throw new IndexOutOfRangeException (); Elsereturn (Bits[index >> 5] & 1 << Index)!= 0} Set{if (Index < 0 | | | Index >= length) throw new IndexOutOfRangeException (); else if (value) Bits[index >> 5] |= 1 << index;elsebits[index >> 5] &= ~ (1 << index);}} Class test{static void Main () {BitArray bits=new BitArray; for (int i=0;i<10;i++) bits[i]= (i%2) ==0;                          Console.Write (bits[i]+ " ");}
Compile and run the program to get the following output:

True false true False to true false true false
The above program provides the user with an interface-friendly bool array with the use of indexers, while significantly reducing the cost of storage space for the program. Indexers are often used in object containers to provide a friendly access interface to the objects within them-which is why C # wraps the methods into indexers. In fact, we can see that indexers have a large number of applications in the. NET Framework class Library.

Operator overloading

An operator is a member of a C # that defines an expression operation between instance objects of a class. Like indexers, operators are still an abstraction of a logical interface to the implementation of a method, meaning that in the Il Intermediate Language code compiled, the operator is still invoked as a method. Defining operator members within a class is also called operator overloading. There are three overloaded operators in C #: Unary operators, two-dollar operators, and conversion operators. Not all operators can be overloaded, and the three operators have the appropriate set of overloaded operators, listed in the following table:

Unary operator +-! ~ + +--true false
Binary operator +-*/% & | ^ << >> =!= > < >= <=
Conversion Operations Fu Yin () and Explicit conversions ()
Overloaded operators must be public and static decorated, or they can cause compilation errors, which are self-evident in the logical semantics of the operator. The overloaded operators of the parent class inherit from the quilt class, but this inheritance does not cover, hide, abstract, and so on, and cannot perform virtual sealed override abstract modification on overloaded operators. Operator must be a pass-value parameter. Let's look at a concrete example here:

Using System;class complex{double  R, v; //r+ v ipublic Complex (Double R, double v) {this.r=r;this.v=v;} public static Complex operator + (Complex A, Complex b) {return new Complex (A.R+B.R, A.V+B.V); public static Complex operator-(Complex a) {return new Complex (-A.R,-A.V);} public static Complex operator + + (Complex a) {  double r=a.r+1;  double V=a.v+1;return new Complex (R, v); public void Print () {Console.Write (r+ "+" +v+ "I");} Class Test{public static void Main () {Complex a=new Complex (3,4); Complex b=new Complex (5,6); Complex C=-a;c.print (); Complex d=a+b;d.print (); A.print (); Complex e=a++;a.print (); E.print (); Complex f=++a;a.print (); F.print ();}}
Compile the program and run to get the following output:

-3 + -4i 8 + 10i 3 + 4i 4 + 5i 3 + 4i 5 + 6i 5 + 6i
Here we implement a "+" number two operator, a "-" unary operator (negative), and a "+ +" unary operator. Notice here that we have not made any changes to the incoming parameters-this is especially important when the parameter is a variable of the reference type, although the parameter of the overloaded operator can only be a value-passing method. And when we return a value, we often need a new variable for "new"--except for the true and false operators. This is especially important when overloading the "+ +" and "--" operators. In other words, when we do the a++, we discard the original a value and replace the new one with a value for a! It is worth noting that the value of E in e=a++ or f=++a, or the value of F, is not related to the return value of our overloaded operator! Their value is simply to get the old or new value of a in front and back! The behavior of the front and back is not difficult to understand.

Operator overloading has a very strict requirement for return values and parameter types. There is only one parameter in the unary operator. The operator "+ +" and "--" return value types and parameter types must be the same type as declaring the operator. Operator "+-! ~ "The parameter type must be the same as the type that declares the operator, and the return value type can be arbitrary. The parameter types of the true and false operators must be the same as the type that declares the operator, and the return value type must be bool and must be paired--that is, declaring only one of them is wrong and causing compilation errors. The difference in parameter types results in an overload of an operator with the same name-actually, this is the performance of the method overload.

The binary operator argument must be two, and both must have at least one parameter type that declares the type of the operator. The return value type can be arbitrary. Three pairs of operators also need to be paired with statements that appear, they are "= =" and "!=", ">" and "<", ">=" and "<=". Note that the types of two parameters are different, although the same type but different order results in the overload of the operator with the same name.

Conversion operators provide implicit conversions and explicit conversions between different types, mainly for method invocation, transformation of expressions, and assignment operations. The conversion operator also has strict requirements for its parameter type (converted type) and return value type (conversion type). The parameter type cannot be the same as the return value type, and must have at least one type that defines the operator. The conversion operator must be defined within any one of the converted type or conversion type. A system-defined transformation operation cannot be redefined. None of the two types can be object or interface types, and there can be no direct or indirect inheritance between them-these three systems have been converted by default. Let's take a look at an example:

Using system;public struct digit{byte value;public Digit (byte value) {if (Value < 0 | | value > 9) throw new Argument Exception (); this.value = value;} public static implicit operator byte (Digit d) {return d.value;} public static explicit operator Digit (byte b) {return new Digit (b);}}
The above example provides an implicit conversion and an explicit conversion between the digit type and the byte type. Conversion from digit to byte is an implicit conversion, and the conversion process does not throw an exception because any information is lost. Conversion from byte to digit is an explicit conversion, and the conversion process may throw an exception because of loss of information. In fact, it also shows us when to declare implicit conversions, and when to declare the design principles of a display conversion. You cannot declare both implicit and explicit conversions on the same parameter type. Implicit conversions and explicit conversions do not need to be paired-although C # recommends doing so.

Can actually see that for attributes, indexers and operators the interface operations that C # provides to us are some form of logical abstract packaging of methods that are designed to provide a user-friendly interface for the type of user we define--we can fully implement the functionality they implement by means of a method. With this design in mind, we will properly and correctly use these operations without causing abuse or misuse.



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.