Nanjing University of Posts and Telecommunications Li Jianzhong (Cornyfield@263.net)
Indexer
Indexer is a new type member introduced by C #, which makes objects that can be conveniently and intuitive as arrays. The indexer is very similar to the attributes we talked earlier, but the indexer can have a list of parameters and can only act on an instance object without directly acting on the class. Below is a typical index design, we have ignored specific implementation here.
Class myclass
{
Public Object this [int index]
{
get
{
// Take data
}
set
{
/ / Save data
}
}
}
The indexer does not have the name like attributes and methods, and the keyword THIS clearly expresses the characteristics of the indexer reference object. Like attributes, Value keyword has parameter transfer in the statement block after SET. In fact, from the compiled IL intermediate language code, the above index is implemented as:
Class myclass
{
Public Object Get_Item (int INDEX)
{
// Take data
}
Public void set_item (int index, object value)
{
/ / Save data
}
}
Since our indexer is compiled into two methods behind GET_ITEM (int index, object value), we can't even declare these two methods in declaring to implement the indexer class, compiler An error is reported to this behavior. Such implied implementations can also be called, inherit, etc., and there is no difference between our own implementation. The compilation implementation of the C # language underlying provides a good foundation for our understanding of the C # indexer.
As with the method, the indexer has five access protection levels, and four inheritance behavior modifications, and external indexers. These behaviors do not have any differences with methods, and will not be described here. The only difference is that the indexer cannot be static, which is easy to understand in the semantics of object references. It is worth noting that when override implements an indexer, it should be used to access the parent class's indexer with Base [E].
Like the realization of attributes, the data type of the indexer simultaneously provides the type of the GET statement block and the type of the Value keyword in the SET statement block.
The parameter list of the indexer is also a matter of paying attention. The "index" is characterized such that the indexer must have at least one parameter, which is located in the middle brackets after the THIS keyword. The parameters of the indexer can only be type value, and there is no REF (reference) and OUT modification. The data type of the parameter can be any data type in C #. C # is based on different parameter signs to perform the polymorphism of the indexer. All parameters in the middle brackets can be referenced under GET and SET, and the value keyword can only be used as a pass parameter under Set.
Below is a specific application example of an indexer, which is helpful to understand the design and application of the indexer.
Using system;
Class BitArray
{
Int [] bits;
Int length;
Public BitArray (int LENGTH)
{
IF (Length <0)
Throw new argumentexception ();
Bits = new int in ((Length - 1) >> 5) 1];
THIS.LENGTH = Length;
}
Public int LENGTH
{
Get {return longth;}
}
Public bool this [int index]
{
get
{
IF (index <0 || index> = length)
Throw new indexoutofrangeexception (); else
Return (Bits [Index >> 5] & 1 << index)! = 0;
}
set
{
IF (index <0 || index> = length)
Throw new indexoutofrangeexception ();
Else if (Value)
Bits [INDEX >> 5] | = 1 << index;
Else
Bits [INDEX >> 5] & = ~ (1 << index);
}
}
}
Class test
{
Static void main ()
{
BitArray bits = new bitaRray (10);
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 True False True False True False
The above program provides the user with an interface-friendly BOOL array through the use of the indexer and greatly reduces the price of storage. Indexers are often used to provide friendly access interfaces for object containers - this is why C # will be packaged into an indexer. In fact, we can see that the indexer has a lot of applications in the .NET Framework class library.
Operator overload
The operator is a member of the expression operation between the example object used to define the example object of the class C #. Similar to the indexer, the operator is still an abstraction of a logical interface to the method, that is, in the compiled IL intermediate language code, the operator is still called in the form of a method. Define operator members in the class is also called an operator overload. There are three overload operators in the C #: one dollar operator, binary operator, and conversion operators. Not all operators can be overloaded, and the three operators have corresponding overload operators, which are listed in the table:
One dollar operator -! ~ - True False
Binary operator - * /% & | ^ << >> ==! => <> = <=
Conversion operator implicit conversion () and explicit conversion ()
The overload operator must be a public and static modifications, otherwise it will cause compilation errors, which is self-evident in the logic language of the operator. The kernel's overload operator will be inherited by subclass, but this inheritance is not covered, hidden, abstract and other behavior, and cannot perform Virtual Sealed Override Abstract to reload operators. The parameters of the operator must be a transmission value parameter. Let's take a specific example:
Using system;
Class Complex
{
Double R, V; // R V i
Public 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 programs and runs to get the following output:
-3 -4i 8 10i 3 4i 4 5i 3 4i 5 6i 5 6i
We have implemented a " " binary operator, a "-" one yuan operator (with a negative value), and a " " one yuan operator. Note that we have no changes to the parameters of the coming - this is especially important in parameters of reference types, although the parameters of the heavy-duty operator can only be a transmission method. And when we return the value, we often need a new variable in "New" - except true and false operators. This is particularly important when overloading " " and "-" operators. That is to say, when we do in A , we will discard the original a value, and replace the value of the new New New New New New New New New New New New New New New New New New New New New New New New New New New New New New New New New New New New New New New New New NEW The value is at all with the return value of the operator we overloaded! Their values are only the old value or new value of A in the pre-preamp and backward! It is not difficult to understand the pre-predetermined and post behavior.
Operator overload has quite strict requirements for return values and parameter types. There is only one parameter in the one-dollar operator. Operators " " and "-" return value types and parameter types must be the same as that declare the operator. The parameter type of the operator " -! ~" Must be as in the same type of the operator, and the return value type can be arbitrary. The parameter type of the True and False operators must be the same as the type of the operator, and the return value type must be BOOL, and must be paired - that is, the one is not correct, it will cause compilation errors. The difference in parameter type can result in overloading of the operator of the same name - actually this is the performance of method overload.
The binary operator parameter must be two, and two must have at least one parameter type to declare the type of the operator. The return value type can be arbitrary. There are three pairs of operators that need to be paired, they are "==" and "! =", ">" And "<", "> =", "<=". It should be noted that the types of the two parameters are different, although the type is the same but the order is different, which will cause the same name of the operator.
The conversion operator provides implicit conversion and explicit conversion between different types, mainly for method call, transformation expression, and assignment operation. The conversion operator has strict requirements for its parameter type (transformed type) and return value type (conversion type). The parameter type and return value type cannot be the same, and there must be at least one of the two and define the type of operator. The conversion operator must be defined inside one of the transformed types or conversion types. You cannot redefine the system-defined conversion operations. The two types cannot be Object or interface types, and there is no direct or indirect inheritance relationship between the two - the three cases have been converted by default. Let's take an example: use system;
Public struct Digit
{
BYTE VALUE;
Public Digit (Byte Value)
{
IF (Value <0 || Value> 9)
Throw new argumentexception ();
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 implicit conversion and explicit conversion between the DIGIT type and the BYTE type. From DIGIT to BYTE to imply conversion, the conversion process does not throw an exception due to any information loss. The conversion from BYTE to DIGIT is explicit conversion, and the conversion process may throw an exception due to loss information. In fact, this also reveals when when declared implicit conversions, and when declared the design principles of the conversion. It is not possible to declare implicit conversion and explicit conversion at the same parameter type. Implicit conversion and explicit conversion do not need pairing - although C # is recommended.
In fact, you can see that for attributes, indexers, and operators These C # provide interface operations, all of which are a form of logical abstraction packaging, which is designed to provide a user-friendly user with a friendly easy Interface - We can fully achieve their implementation through methods. Understand such design original intentions, we will properly use these operations correctly, and not cause abuse and misuse.