Every 10 years, programmers need to spend a lot of time and effort to learn new programming technology. In the 1980s, UNIX and C, the 1990s is Windows and C , and now it is round to Microsoft's .Netframework and C #. Although it is necessary to learn new technologies, the benefits that have brought are much higher than the labor. Fortunately, most of the engineering of C # and .NET is not in nature in Nature in C and WINDOWS. In this article, I will show how to achieve leaps from C to C #.
There have been many articles to introduce C # to C improvements, here I will no longer repeat these problems. Here, I will focus on the biggest change at C # when C turned C #: Changes from unmanable environment to manageable environments. In addition, I will also propose a mistake for C # programmers to make a reference for everyone, in addition, some C # language will affect the programming.
Steering manageable environment
C design goals are low-level, object-oriented-oriented programming languages that are not related to the platform, and C # is an advanced facing-oriented programming language. The transformation to the manageable environment means that your programming method thinks, C # no longer processes subtle control, but let the architecture help you handle these important issues. For example, in C , we can use New in the stack, in the stack, or even a certain location in memory in memory.
In the .NET's manageable environment, we don't have to control that fine control. After selecting the type to create, its location is fixed. Objects for simple types (INTS, DOUBLE and long) are always created in the stack (unless they are included in other objects), the class is always created in the heap. We can't control the object which location is created which position in the heap, and there is no way to get this address, and the object cannot be placed in a certain location in memory. (Of course, there is also a way to break through these restrictions, but that is an alternative method.) We can no longer control the survival cycle of the object, C # has no Destructor. The debris collection program reclaims the memory occupied by the object, but this is not explicitly carried out.
It is this structure that is C # reflects its infrastructure, with no multiple inheritance and template, because multiple inheritance is difficult to implement in a manageable fragment collection environment.
The simple type in C # is just a simple mapping of types of types (CLRs) in the General Language Runtime (CLR), for example, INT in C # is a mapping for System.InT32. The data type in the C # is not determined by the language itself, but is determined by the CLR. In fact, if you still want to use the object created in the VisualBasic in C #, you must make your programming habits more in line with the CLR regulations.
On the other hand, manageable environment and CLR have also brought us advantages. In addition to fragment collection and all .NET language unified data types, it also provides us with a powerful component-oriented programming language that does not require special support for later bindings, type discovery and post-binding are built-in In the language. Attributes are the members, events, and agents of the first class in the C # language.
The most important advantage of managed environment is .Netframework. Although this framework can be used in all .NET languages, C # can better use rich classes, interfaces, and objects in the .NET framework.
TRAPS
C # looks very similar to C , which makes us relax when we turn to C # by C , but there are some places where it is easy to make mistakes. The code is very beautiful in C , which will not be compiled in C #, and even unexpected results. C # and C change in grammar, the compiler can find most of the differences between the two, I am not more ink here, here I introduce several more prone to problems. Comparison Important changes: reference types and value types
In C #, the value type and reference type data are different. Simple type (int, long, double, etc.) and structures belong to value type data, classes, and objects belong to reference type data. Unless it is included in a variable included in the reference type, the value of the value type variable is stored in the stack as in C . The variable of the reference type is also stored in the stack, but its value is an address of the object stored in the heap, which is similar to C . Value type variables pass their values to methods, and reference type variables pass their own pointers to methods.
structure
The structure in C # has a very obvious difference from C . In C , the structure is more like class, except for the default inheritance, its default access is public instead of private. In C #, the structure is quite different, it is used to encapsulate light objects, the data type of the value type, which is transmitted when transmitting the value of the variable, not its address. In addition, they also have some unsuitable limits, for example, it cannot be inherited, and there is no basic class other than System.ValeType. The structure cannot define a default constructor.
On the other hand, since the structure is high, it is highly efficient, so it is ideal for creating a lightweight object. Therefore, if its shortcomings have no effect on your software, the use structure is much higher than the use efficiency, especially for small objects.
Everything is object
In C #, all things are variables that are obtained by inheriting Object, including category and int, structs equivalents. The Object class provides some useful methods, such as Tostring, an example of using toString is used with System.Console.Writeline, which can accept a string and many objects. Unlike the use of the PrintF statement, you want to use WriteLine, you need to provide a converted variable. Suppose MyEmployee is an instance of user-defined Employee classes, MyCounter is an instance of user-defined Counter classes:
Console.writeline ("TheEmployee: {0}, ThecounterValue: {1}", myemployee, mycounter;
Where WriteLine will call the object.tostring method of each object, replace the variables returned as parameters. If the Employee class does not overwrite toString, the default implementation is called (inherited by system.object), which will return the name of the class as a string. Counter will override totring, return a constant variable, so the output of the above code is:
TheEmployee: Employee, ThecounterValue: 12
What happens if a integer variable is transmitted to WRITELINE? Since TOSTRING cannot be called to integer variables, the compiler will automatically package integer variables in an example of an object. When WriteLine calls toString, the object returns a string representing the integer variable value. The following code illustrates this problem: use of class
usingSystem; // not covered ToString class publicclassEmployee {} // class covers ToString publicclassCounter {privateinttheVal; publicCounter (inttheVal) {this.theVal = theVal;} publicoverridestringToString () {Console.WriteLine ( "CallingCounter.ToString ()" ); returntheval.tostring ();}} publicclasstester {publicstaticvoidMain () {// Create a class of instance test = newtester (); // call non-statist member // (MustbethRoughanInstance )t.run(); }// demo call the non-static method ToString publicvoidRun () {EmployeemyEmployee = newEmployee (); CountermyCounter = newCounter (12); Console.WriteLine ( "Theemployee: {0}, thecountervalue: {1}", myEmployee, myCounter); intmyInt = 5; Console .Writeline ("HereareTwointegers: {0} and {1}", 17, myint);}}
Reference type parameters and output parameters
The same as C , the method in C # can only have a return value. In C , we overcome this limitation by using the pointer or index as a parameter, the method called the method changes the parameters, the call method can get a new value.
When transmitting an index as a parameter to the method, the original object can only be accessed in a manner that can be provided in the delivery index or pointer. This approach cannot be employed for value type variables. If you want to pass a value variable by a reference parameter, you need to add REF genuine characters in front. As follows:
PublicvoidgetStats (Refintage, Refintid, Refintyearsserved)
It should be noted that it is necessary to use the REF genuine word in the definition of the method, but also need to use the REF genuine word in the actual call to the method.
Fred.getStats (Refage, Refid, Refyearsserved);
Now we can define the AGE, ID, and Yearsserved variables in the call method, and pass them to getStats to get the changed value.
C # requires a clear assignment, that is, before calling the getStats method, you must initialize the three local variables of Age, ID and YearsserveD, which seems to be a bit, because we only use them to get new variables from getStats. Value. In order to solve this problem, C # provides OUT care, indicating that we can pass the variables that are not initialized in the method, which will be passed by reference variables:
PublicvoidgetStats (Outintage, OutIntid, Outintyearsserve)
Of course, the calling method must also make corresponding changes:
Fred.getStats (Outage, Outid, Outyearsserved);
New call
In C , the New Getting Critical word can generate an object on the heap. This is not the case in C #. For reference type variables, New Get Takes aims to generate an object on the heap; for structural equivalence type variables, New Get Takes Generate an object in the stack and needs to call constructor. In fact, we can generate a structural type variable on the stack without using new care, but it should be noted that the New Gettingtick can initialize the object. If NEW is not used, you must initialize all members of the structure before use, otherwise it will be wrong when compiling.
Object initialization
usingSystem; // There are two member variables and a simple structure constructor publicstructPoint {publicPoint (intx, inty) {this.x = x; this.y = y;} publicintx; publicinty;} publicclassTester {publicstaticvoidMain () {Testert = newtester (); T.Run ();} publicvoidrun () {pointp1 = newpoint (5, 12); SomeMethod (p1); // FinePointP2; // Do not call New and create // compile to compile it here It will be wrong because the member variables of P2 are not initialized // SomeMethod (P2); // manually initialize P2.x = 1; p2.y = 2; SomeMethod (p2);} // A Can accept Point as Parameter Method PrivatevoidsomeMethod (Pointp) {Console.writeline ("POINTAT {0} x {1}", PX, PY);}}
Attributes
Most C programmers want to make the attributes of the member variables are Private, which promotes the emergence of data package concepts, allowing us to change the implementation of the class without changing the user's dependency. Typically, we only want customers to get or set values for these member variables. Therefore, the C programmer has developed an accessor used to access the Private member variable.
In C #, attribute is the first level of the class. For customers, attribute look like a member variable. For the implementation of the class, it looks more like a method. This design is very clever, which can achieve both data hidden and encapsulation, but also enable customers to easily access member variables.
We can add an AGE attribute in the Employee class to make it easy for customers to get and set members of the employee age:
Publicintage {get {rary;} set {age = value;}}
Guan Jian Value can be hookedly used in the property. If you write the following code:
Fred.age = 17;
The compiler will pass the value 17 to value.
By using GET without using SET, we can create a read-only attribute for Yearsserved:
publicintYearsServed {get {returnyearsServed;}} Accessors using privatevoidRun () {EmployeeFred = newEmployee (25,101,7); Console.WriteLine ( "Fred'sage: {0}", Fred.Age); Fred.Age = 55; Console .Writeline ("Fred'sage: {0}", Fred.age); console.writeline ("Fred'sservice: {0}", Fred.YearSserved); // Fred.yearsserved = 12; // is not Allowed}
We can get the age of FRED through attributes, or you can use this property to set the age. Although we can access the Yearsserved property to get its value, but cannot set the value. If you don't comment out the code of the last row, you will be wrong when you compile. If we will decide to get the age of Employee from the database, we only need to change the implementation of the accessor, and the customer will not be affected.
Array
C # provides an array class, which is more intelligent than traditional arrays in C / C . For example, the boundary is not exceeded when writing an array in a C #. In addition, the array has a smarter partner -Araylist, which can dynamically grow and manage the changing demand for array size.
There are three forms in the array of C #: a one-dimensional array, a multi-dimensional uniform group (like the traditional array in C ), a non-uniform array (array of arrays). We can create a one-dimensional number of groups by the following code:
int [] MyintArray = newint [5];
In addition, it can be initialized in the following manner:
int [] MyintArray = {2, 4, 6, 8, 10};
We can create a uniform number of 4 × 3 in the following way:
Int [,] MyRectang Taray = newint [rows, columns];
We can initialize the array as follows:
Int [,] myRectang Taray = {{0, 1, 2}, {3, 4, 5}, {6, 7, 8}, {9, 10, 11}};
Since the non-uniform array is an array of arrays, we can only create a one-dimensional uniform number of arrays:
int [] [] myjaggedArray = newint [4] [];
Then create each array of inside:
MyjaggedArray [0] = newint [5]; myjaggedArray [1] = newint [2]; myjaggedArray [2] = newint [3]; myjaggedArray [3] = newint [5];
Since the array is obtained by inheriting the System.Array object, they have many useful methods including Sort, Reverse.
Indexer
We can create an object like an array. For example, we can create a list box showing a series of strings, you can use a list box as an array, using an index, you can easily access the contents in the list box.
StringthefirstString = mylistbox [0]; stringthelaststring = myListbox [length-1];
This is done by the indexer. The indexer is largely like an attribute, but supports the syntax of the index operation. Figure 4 shows an attribute that follows the index operator, and Figure 5 shows how to complete a very simple ListBox class and index it:
interface
The software interface is a contract between the two objects interact. If an object releases an interface, it is equal to all possible customers: I support the following methods, properties, events, and indexers.
C # is an object-oriented language, so these contracts are encapsulated in an entity called an interface, and the interface defines the object of the cited type type of the contract. In terms of concept, the interface is very similar to the abstraction class. The difference between the two can be an abstract class that can be used as a series of derivatives, and the interface is combined with other inherited trees.
Ienumerable interface
Go back to the example above. As in a normal array, use the Foreach-Loop loop structure to print the string in the listboxtest class, which can be implemented by implementing the IEnumerable interface in the class, which is implicitly completed by the Foreach-Loop loop structure. of. The IEnumerable interface can be implemented in any class that supports enumeration and foreach-loop cycles. There is only one method GetEnumerator with the IEnumerable interface, and its task is to return a special IENUMERATOR implementation. From a grammatical perspective, the ENUMERABLE class provides an IEnumerator.
Figure5ListBoxClassusingSystem; // simplified ListBox control publicclassListBoxTest {// initialize the string ListBoxpublicListBoxTest (paramsstring [] initialStrings) {// allocate space for the string myStrings = newString [256]; // copy the string to the constructor foreach (stringsininitialStrings) {myStrings [myCtr ] = s;}} // add a string publicvoidAdd (stringtheString) {myStrings [myCtr ] = theString at the end of the ListBox;} publicstringthis [intindex] {get {if (index <0 || Index> = mYStrings.Length) {// Processing an index} ReturnMystrings [index];} set {mystrings [index] = value;}} // Return how many strings PublicintGetNumentries () {ReturnMyctr;} privateString ] mystrings; privateclasstester {staticvoidMain () {// Create a new list and initialize listboxtestlbt = newlistboxtest ("Hello", "world"); // Add some new string lbt.add ("WHO" ); lbt.add ("is"); lbt.add ("john"); lbt.add ("galt"); stringsubst = "universe"; lbt [1] = subs; // Access all string for (inti = 0; i
{
Console.writeline ("LBT [{0}]: {1}", i, lb [i]);
}
}
}
Enumerator must implement the IEnumerator method, which can be implemented directly through a container class or a separate class, and the latter method is often selected because it can encapsulate this task in the Enumerator class without making the container class very confusing. . We will add an ENUMERATOR class in the listboxtest in the above code. Since the Enumerator class is for our container class (because the ListBoxEnumerator must know many of the ListBoxTest), we will make it unapproved in Listboxtest. In this example, Listboxtest is defined to complete the IEnumerable interface, and the IEnumerable interface must return an ENUMERATOR.
Publicienumeratorgeetenumerator () {return (ienumerator) newlistboxenumerator (this);}
Note that the method passes the current ListboxTest object (this) to ENUMERATOR, which will enumerate the ENUMERATOR to enumerate the elements in this designated listboxtest object. ENUMERATOR implementing this class is implemented here for ListBoxEnumerator, which is defined as a private class in Listboxtest, which is quite simple.
The enumerated listboxtest is passed as a parameter to the constructor. Listboxtest is assigned to the variable MYLBT, and the constructor will set the member variable index to -1, indicating that the enumeration of the object has not started.
PublicListBoxEnumerator (listboxtestthelb) {mylbt = thelb; index = -1;}
The MOVENEXT method adds 1 to INDEX and then make sure there is no boundary of the enumerated object. If the boundary is exceeded, the false value will be returned, otherwise the TRUE value is returned.
PublicBoolMoveNext () {index ; if (index> = mylbt.mystrings.Length) ReturnFalse; ElsereturnTrue;}
The role of the RESET is just set to -1 of the value of INDEX.
Current Returns a recently added string, which is an arbitrary setting. In other classes, Current can have the meaning of designers. Whether it is designed, each enumerated method must be able to return the current member.
PublicobjectCurrent {get {return (mylbt [index]);}}
The call to the Foreach loop structure can obtain the method of enumeration and use it to handle each member in the array. Since the Foreach loop structure will display each string, no matter whether we add a meaningful value, we will change the initialization of MyStrings to 8 entries to ensure that the display is easy to handle.
MyStrings = News TRING [8];
Use basic class libraries
In order to better understand the difference between C # and C and solve the problem, let's first look at a relatively simple example. We will create a class that read the text file and displays its content on the screen. I will make it a multi-threaded program to do other work when reading data from the disk.
In C , we may create a thread of the read file and another thread to do other work, which will run independently, but may need to synchronize them. In C #, we can also complete the same work, because the .NET framework provides a powerful asynchronous I / O mechanism, we will save a lot of time when writing a thread.
Asynchronous I / O Support is built in the CLR and is almost as simple as using normal I / O streams. At the beginning of the program, we first inform the compiler, we will use many objects in the program:
USINGSYSTEM; USINGSYSTEM.IO; USINGSYSTEM.TEXT;
In the program contains System, it does not automatically contain all its sub-namespaces, and uses use of useful authentication to express each child name space. We use I / O streams in the example, so we need to include system.io namespace, we also need System.Text namespace to support the by-life ASCII code.
Since the .NET architecture is to complete most of the work, the steps needed to write this procedure are quite simple. We will use the BeginRead method of the Stream class, which provides an asynchronous I / O function, read the data into a buffer, when the buffer can process the corresponding handler.
We need to use a byte array as a buffer and callback method, and define both as the private member variable of the driver class. Publicclassasynchiotester; privatebyte [] buffer; privateasyncCallbackmyCallback
InputStream is a STREAM type variable, we will call the BeginRead method for it. The agency is very similar to the pointer to the member function. The agent is the first type of element of C #.
When the buffer is filled with files on the disk, .NET will process the data to the proxy method. We can make your computer to do other jobs during the read data. (In this example, one integer variable is added from 1 to 50000, but in actual applications, we can make computers interact with users or make other meaningful work.)
The proxy in this example is defined as the ASYNCCALLBACK type process, which is required for the Stream's BeginRead method. The definition of the AsyncCallback type agent in System Space is as follows:
PublicdelegatevoidasyncCallback (IASYNCRESULTAR);
This agent can be associated with any method of returning a Void type, which is associated with the IASYNCRESULT interface as a parameter. When the method is called, the CLR can pass the IASYNCRESULT interface object as a parameter at runtime. We need to define this method as follows:
VoidOncompletedRead (IasyncResultasyncResult)
Then connect to the agent in the constructor:
Asynchiotester () {??? mycallback = newasynccallback (this.oncompletedready);
The above code assigns an instance of the agent to the member variable mycallback. Here is the detailed working principle of all processes. In the main function, create a class instance and let it start running:
Publicstaticvoidmain () {asynchiotesterTheapp = newasynchiotester (); theapp.run ();}
New Guttles can start the constructor. In the constructor, we open a file and get a stream object. Then allocate space in the buffer and coupled with the callback mechanism.
Asynchiotester () {INPUTSTREAM = file.openread (@ "c: /msdn/fromcpptocs.txt"); buffer = newbyte [buffer_size]; mycallback = newasyncCallback (this.oncompletedRead);}
In the RUN method, we call BeginRead, which will read files asynchronously.
InputStream.Beginread (buffer, // storage result 0, // offset buffer.length, // buffer in the bufferback, // callback agent null); // Local object
At this time, we can do other work.
For (longi = 0; i <50000; i ) {if (i% 1000 == 0) {Console.WriteLine ("i: {0}", i);}}
After the file read operation is over, the CLR will call the callback method.
VoidOncompletedRead (IasyncResultasyncResult) {
The first thing to do in OnCompletedRead is how many bytes have been found by calling the endread method of the Stream object:
INTBYTESREAD = INPUTSTREAM.ENDREAD (AsyncResult);
The call to the endread will return the number of bytes read. If the number returned is larger, convert the buffer to a string, then write it on the console, then call BeginRead again, start another asynchronous process. IF (BytesRead> 0) {strings = encoding.ascii.getstring (buffer, 0, bytesread); console.writeline (s); InputStream.Beginread (buffer, 0, buffer.length, mycallback, null);}
Now, in the process of reading the file, you can do a good job (in this case from 1 to 50000), but we can process the read data in each buffer (in this example) The data in the buffer is output to the console). Interested readers can click here to download the full source code.
The management of asynchronous I / O is completely provided by the CLR, so that it is better when reading the file on the network.
Read files on the network
In C , read files on the network requires considerable programming skills, .NET provides a wide range of support. In fact, reading files on the network is just another application of the Stream class in the base class library.
First, in order to monitor the TCP / IP port (65000 in this example), we need to create an instance of a TCPListener class.
TCPListenertCplistener = NewTCPListener (65000);
Once created, let it start to listen.
TCPListener.start ();
It is now waiting for the customer connection.
SocketSocketForClient = tcplistener.accept ();
The ACCEPT method of the TCPListener object returns an Socket object, and Accept is a synchronized method unless the received request will return. If the connection is successful, you can start sending a file to the customer.
IF (socketforclient.connected) {???
Next, we need to create a NetWorkStream class and pass the road to the constructor:
NetWorkStreamNetworkStream = NewNetworkStream (socketforClient);
Then create a StreamWriter object, just create the object this time not on the file but is created on the NetWorkStream class created:
System.io.streamwriterstreamwriter = newsystem.io.StreamWriter (NetworkStream);
When the content is written to the flow, the flow is transmitted to the client through the network.
Creation of the client
The client software is a specific example of a TCPClient class, and the TCPCLIENT class represents a TCP / IP connection to the host.
TcpClientSocketforserver; socketforserver = NewTcpClient ("LocalHost", 65000);
With the TCPClient object, we can create a NetworkStream object, then create a StreamReader class on it:
NetworkStreamNetworkStream = socketforserver.getStream (); System.io.StreamReaderstreamReader = newsystem.io.streamReader (NetworkStream);
Now, as long as it is to read the stream, the result is output to the console.
Do {OutputString = streamreader.readline (); if (outputstring! = null) {Console.writeline (OutputString);}} while (outputString! = null); In order to test this code, you can create the following test file:
THISLINEONETHITHITHITWOTHISLINETHREETHISLINEFOUR
This is the output from the server:
Output (Server) ClientConnectedSendingthislineOndingThislineEtwoseslinethislinethresendingthislinefourDisconnectingFromClient ... EXITING ...
Below is an output from the client:
THISLINEONETHITHITHITWOTHISLINETHREETHISLINEFOUR
Properties and metadata
A significant difference between C # and C is that it provides support for metadata: data about other entities such as classes, objects, methods. Properties can be divided into two categories: a class appears in the form of a part of CLR, the other is the property we created by our own, and the CLR property is used to support serialization, arrangement, and COM synergy. Some attributes are for a combination, and some attributes are for class or interface, which are also referred to as an attribute target.
In order to place the property in square brackets before the property target, the attribute can act on their attribute targets.
[assmbly: assemblydeeness (false)] [assembly: askEMBLYKEYFILE (".// Keyfile.snk")]
Or use a comma to separate each property:
[assembly: assemblydeelaysign (false), assembly: askEMBLYKEYFILE (".// Keyfile.snk"]
Custom attribute
We can use them to create custom properties and use them when considering appropriate. Suppose we need to track the repair of bugs, you need to build a database that contains bugs, but you need to bind the bug report to a special correction situation in a piece, you may add the following comments shown below:
// bug323fixedbyjesseliberTy1 / 1/2005.
Thus, you can understand the correction of the bug in the source code, but if you save the relevant information in the database, it may be better, so it is more convenient for our inquiry. If all BUG reports use the same syntax, it is better, but we need a customized property. We may use the following content instead of the comment in the code:
[Bugfix (323, "Jesseliberty", "1/1/2005") Comment = "offbyoneerror"]
Like other elements in C #, attributes are also classes. Customized attribute classes need to inherit System.attribute:
Publicclassbugfixattribute: System.attribute
We need to let the compiler know what type of element can be used, and we can specify this type of element in the following way:
[AttributeUSAG (AttributeTargets.classmembers, AllowMultiple = true)]
AttributeUSage is an attribute ━━ attribute that acts on attributes, which provides metadata of metadata, namely data about metadata. In this case, we need to pass two parameters, the first is the target (in this example is a class member.), The second is whether a given element can accept more than one attribute. The value of the allowmultiple is set to True, meaning that the class member can have more than a bugfixattribute property. If you want to fracture in combination, you can use the OR operator to connect them. [AttributeUSAGE (AttributeTargets.class | AttributeTargets.Interface, allowmultiple = true)]
The above code will enable a property to a class or an interface.
The new custom attribute is named bugfixattribute. The named rule is to add Attribute after the property name. After assigning the property to an element, the compiler allows us to call this property using a streamlined attribute name. Therefore, the following code is legal:
[Bugfix (123, "Jesseliberty", "01/01/05", Comment = "Offbyone")]
The compiler will first look for the name of Bugfix, and if you do not find it, look for bugfixattribute.
Each attribute must have at least one constructor. Attributes can accept two types of parameters: environmental parameters and naming parameters. In the previous example, bugid, programmer's name and date are environmental parameters, and comments are named parameters. Environmental parameters are passed to the constructor and must be passed in the order defined in the constructor.
PublicbugfixAttribute (intbugid, stringprogrammer, stringdate) {this.bugid = bugid; this.programmer = programmer; this.date = date;} namedparametersareImplementASproperties.
Use of attributes
In order to test the attribute, we create a simple class named mymath and add two functions to it, then specify the bugfix property.
[Bugfixattribute (121, "Jesseliberty", "01/03/05")] [Bugfixattribute (107, "Jesseliberty", "01/04/05", Comment = "fixedoffbyoneerrors"] PUBLICCLASSMYMATH
These data will be stored with metadata. The following is a complete source code and its output:
Custom attribute
usingSystem; // Create a class member is assigned to custom properties [AttributeUsage (AttributeTargets.Class, AllowMultiple = true)] publicclassBugFixAttribute: System.Attribute {// custom attribute constructors publicBugFixAttribute positional parameters (intbugID, stringprogrammer, stringdate) {this.bugID = bugID; this.programmer = programmer; this.date = date;} publicintBugID {get {returnbugID;}} // parameter name attribute publicstringComment {get {returncomment;} set {comment = value;}} publicstringDate {get {returndate;}} publicstringProgrammer {get {returnprogrammer;}} // private member data privateintbugID; privatestringcomment; privatestringdate; privatestringprogrammer;} // the attribute assigned to the class [BugFixAttribute (121, "JesseLiberty", "01/03 / 05 ")] [BugFixAttribute (107," JesseLiberty "," 01/04/05 ", Comment =" Fixedoffbyoneerrors ")] publicclassMyMath {publicdoubleDoFunc1 (doubleparam1) {returnparam1 DoFunc2 (param1);} publicdoubleDoFunc2 (doubleparam1) {returnparam1 / 3;} }publicclasstester }publicstaticvoidmain(); "; console.writeline("callingdofunc(7).Result: }", mm.dofunc1 (7));}} output:
Callingdofunc (7) .Result: 9.3333333333333339
As we see, the property does not affect the output, and the creation property does not affect the performance of the code. So far, readers are only listening to me to discuss issues related to attributes, using iLDASM browsing metadata, it will find that the property does exist.
Mapping
In many cases, we need a method that can access properties from metadata, and C # provides support for mapping to access metadata. By initializing the MemberInfo type object, this object in the System.Reflection name space can be used to discover the attributes of the member, access the metadata.
System.Reflection.memberinfobalf = TypeOf (MyMath);
Call the TypeOf operator for mymath type, which returns a variable of the Type type generated by inheriting MemberInfo.
The next step is to call getCustomAttributes for the MemberInfo object, and pass the type of attribute you want as a parameter to getCustomAttributes. We will get an object array, and the types of each member of the array are bugfixattribute.
Object [] attributes; attributes = attribute.getcustomattributes (inf, typeof (bugfixattribute);
We can traverse this array, print the array of BugfixAttribute objects, the code as shown in the property: Print
Publicstaticvoidmain () {mymathmm = newmymath (); "CALLINGDOFUNC (7) .Result: {0}", mm.dofunc1 (7)); // Get member information and use it to access custom properties System. Reflection.MemberInfoinf = typeof (MyMath); object [] attributes; attributes = Attribute.GetCustomAttributes (inf, typeof (BugFixAttribute)); // iterate through all attributes foreach (Objectattributeinattributes) {BugFixAttributebfa = (BugFixAttribute) attribute; Console.WriteLine ( "/nbugid: {0 }" ,bfa.bugid ); console.writeline ("Programmer:} }" ,bfa.programmer );console.writeline ("Date: }" ,bfa.date ); console. WriteLine ("Comment: {0}", bfa.comment);}}
Type discovery
We can study the contents of a combined entity by a mapping method, and if you want to establish a tool that needs to display the internal information of the assembly or dynamically call the composition, this method is very useful.
Through the mapping method, we can know the type of modules, methods, domain, attributes, and signals for each method of this type, which supported interfaces and super classes of this class. We can dynamically load a combination with assembly.load static methods in the following form:
PublicStaticassembly.Load (AssemblyName)
You can then pass it to the core library.
AskEMBLYA = Assembly.load ("mscorlib.dll");
Once the assembly is loaded, we can return a TYPE object array by calling gettypes. The Type object is the core of the map, which represents the type definition of classes, interfaces, arrays, values, and enumerations.
TYPE [] TYPES = a.gettypes ();
The combination is returned to a type of array, we can use the Foreach-Loop structure to display the array, and its output will have a few documents, below, we will find a small paragraph from it:
TypeisSystem.TypeCodeTypeisSystem.Security.Util.StringExpressionSetTypeisSystem.Text.UTF7Encoding $ EncoderTypeisSystem.ArgIteratorTypeisSystem.Runtime.Remoting.JITLookupTable1205typesfound
We have received an array of content as the type of core library, which can be printed out, and the array will have 1205 items.
For a type of map, we can also map one type in the assembly. To do this, we can use the GetType method to parse a type from the assembly:
Publicclasstester {publicclasstester {publicstaticvoidmain () {// Check an object TypetHetYpe = type.gettype ("System.Reflection.Assembly); console.writeline (" / nsingletypeis {0} / n ", THETYPE);}}
The output is as follows:
Singletypeissystem.reflection.assembly discovery member
We can also get the type of all members, display all methods, properties, domains, the following code demonstrate the code that implements the above object.
Figure9GettingAllMemberspublicclassTester {publicstaticvoidMain () {// check a single object TypetheType = Type.GetType ( "System.Reflection.Assembly"); Console.WriteLine ( "/ nSingleTypeis {0} / n", theType); // get all of the members MemberInfo [] mbrInfoArray = theType.GetMembers (BindingFlags.LookupAll); foreach (MemberInfombrInfoinmbrInfoArray) {Console.WriteLine ( "{0} isa {1}", mbrInfo, mbrInfo.MemberType.Format ());}}}
Although the resulting output is still very long, we can get the domain, method, constructor, and attributes of the following unwilling people, methods, methods, constructors, and properties:
System.strings_localfileprefixisafieldBooleanIndefined (System.Type) isamethodvoid.ctor () isaconstructorsystem.StringCodeBaseisapropertySystem.StringCopiedCodeBaseisaproperty
Only discovery method
We may only care about the method, not careful, attribute, etc. For this reason, we need to delete the following to getMembers:
MemberInfo [] mbrinfoarray = THYPE.GETMEMBERS (BindingFlags.lookupAll);
Then add the statement that calls getMethods:
Mbrinfoarray = THETYPE.GETMETHODS ();
Now, there is only a method left in the output.
Output (excerpt) BooleanEquals (System.Object) isaMethodSystem.StringToString () isaMethodSystem.StringCreateQualifiedName (System.String, System.String) isaMethodSystem.Reflection.MethodInfoget_EntryPoint () isaMethod
Discover specific members
Finally, in order to further narrow the range, we can use the Findmembers method to discover a certain type of specific way. For example, in the following code, we can only search for a method starting with "GET".
publicclassTester {publicstaticvoidMain () {// check a single object TypetheType = Type.GetType ( "System.Reflection.Assembly"); // Get only acquired at the beginning of the members MemberInfo [] mbrInfoArraytheType.FindMembers (MemberTypes.Method, BindingFlags. Default, Type.Filtername, "get *"); more (Member) {Console.writeline ("{0} ISA {1}", MBrinfo, Mbrinfo.membertype.Format ());}}}
Some of its output is as follows:
System.Type [] GetTypes () isaMethodSystem.Type [] GetExportedTypes () isaMethodSystem.TypeGetType (System.String, Boolean) isaMethodSystem.TypeGetType (System.String) isaMethodSystem.Reflection.AssemblyNameGetName (Boolean) isaMethodSystem.Reflection.AssemblyNameGetName () isaMethodInt32GetHashCode () ISAMETHODSYSTEM.Reflection.Assemblygetssembly (System.Type) IsamethodSystem.TypegetType (System.String, Boolean, Boolean) Isamethod Dynamic call
Once a method is found, you can call it using the mapping method. For example, we may need to call the COS method in system.math (return a cosine value of one angle). To do this, we need to get the type information of the System.Math class, as shown below:
TypethemathType = type.gettype ("system.math");
With type information, we can dynamically load an instance of a class:
Objecttheobj = activator.createInstance (ThemathType);
CREATEINSTANCE is a static method of the Activator class that can be used to initialize objects.
With the instance of the System.math class, we can call the COS method. We also need to prepare an array of defined parameter types because COS only needs a parameter (an angle of seeking cosine values), so there is only one member in the array. We will give a type object to a System.double type in an array, that is, the type of parameters required by the COS method:
TYPE [] paramtypes = newtype [1]; paramtypes [0] = type.gettype ("system.double");
Now we can pass the name of the method, this array defines the type of parameter of the getMethod method in the Type object:
MethodInfocosineInfo = ThemathType.getMethod ("COS", paramtypes);
We now get a MethodInfo type object, we can call the appropriate method there. To do this, we need to pass the actual value of the parameters in the array:
Object [] parameters = newobject [1]; parameters [0] = 45; Objectreturnval = CosineInfo.invoke (theobj, parameters);
It should be noted that I created two arrays, the first name is the type of parameters for parameters, and the second name saves the actual parameter value for the array of Parameters. If the method requires two parameters, we need to keep this two arrays each of the two arguments. If the method does not require parameters, we still need to create these two arrays, just do not need to store data inside.
TYPE [] paramtypes = newtype [0];
Although it looks a bit strange, it is correct. Here is a complete code:
Use of mapping method
usingSystem; usingSystem.Reflection; publicclassTester {publicstaticvoidMain () {TypetheMathType = Type.GetType ( "System.Math"); ObjecttheObj = Activator.CreateInstance (theMathType); // array with only one member of the Type [] paramTypes = newType [1] Paramtypes [0] = type.gettype ("system.double"); // Get information about the COS () method MethodInfocosineInfo = ThemathType.getMethod ("cos", paramtypes); // Fill in the actual parameters in an array Object [] parameters = newobject [1]; parameters [0] = 45; ObjectReturnval = cosineinfo.invoke (theobj, parameters); console.writeline ("Thecosineofa45deGreeangle {0}", returnval);}}
Although there are many small mistakes waiting for C programmers to make, but C #'s syntax and C are not too large, and the conversion to the new language is quite easy. The interesting part of using C # is to use a universal language run, which can only involve several key issues. CLR and .NETFramework provide more support for threads, collection, Internet application development, Windows-based application development, etc. The distinction between language functions and CLR features is very vague, but the combination is a very powerful development tool.