Introduction
Reflection is. NET provides us with a powerful weapon, although in most cases we are not used to reflection, although we may not need to be proficient in it, the use of reflection may be useful in the future development.
Reflection is a huge topic, involving a lot of knowledge points, including assemblies, custom features, generics, etc., want to fully grasp it very difficult. This article is only a brief introduction to reflection, about its more profound content, need to gradually grasp in practice. This article will be divided into the following sections of the introduction. NET Reflection:
- Preface, I will use an example to elicit a reflection and get the first impression of reflection.
- Reflection preliminary, type class, Reflection normal type. (Modified, recently released ...)
- Reflection characteristics (Attribute).
- XXXX (tbd)
- ...
Preface Chapter
If you haven't been exposed to reflection, and I'm now going to give you a bunch of definitions to tell you what reflection is, I'm sure you'll have a blow feeling. I have always believed that the axioms of definitions and concepts can work well only when you fully understand them. So, let's look at a common problem in development and see how to use reflection to solve:
In the process of database design, there are often some basic information tables, such as: The country's cities, or the status of orders. Suppose we name the city's table, which usually contains a field like this:
ID Int Identity (city ID)
Name Varchar (50) city names
Zip Varchar (10) City ZIP code
...//slightly
This table will be referenced by many other tables. If we are building a hotel reservation system, the hotel information sheet will refer to this table and use the Cityid field to refer to the hotel's city. In the case of city tables, the records stored in the table (city information) are uncertain, meaning: we may add a new city to this table at any time (when the first hotel in a city wants to join the booking system, it needs to be added to the city of the new hotel in the cities table). At this point, such a design is reasonable.
1. Building tables and their problems
Let's look at another situation, we need to identify the status of the hotel reservation: uncommitted, submitted, canceled, accepted, returned, booked, expired. At this point, many developers will create a small table in the database called Bookingstatus (reservation status), and then add the status as above, as if:
Like the city table, in other tables in the system, such as the hotel order form (Hotelorder), the table is referenced by the field Statusid to obtain the hotel reservation status. However, a few months later, although it looked like the use of the city table, it turned out that the table was only a federated query in the database, or only in the program, but never changed, since the booking process was generally not changed after it was determined. In the application, the user will not be provided with the table records of the deletion and modification of the interface.
This is often the case when calling this table in a program: we need to filter the list of orders based on the booking status. The usual practice is to use a drop-down menu (DropDownList), the menu's data source (DataSource), we can easily get through a SqlDataReader, we will DropDownList text to the Status field , set the value as the ID field.
At this point, we should have found the problem:
- If we have a flight reservation, a cruise booking, or some other status, we need to create a number of similar small tables in our database, resulting in too many database tables.
- When we use controls such as DropDownList to get table content, we need to connect to the database for querying, potentially impacting performance.
At the same time, we have noticed three points:
- This table is typically used in database union queries. Suppose we have a hotelorder table representing the order of the hotel, which contains the Statusid field representing the status, our query might look like this: SELECT *, (select Status from bookingstatus Where Id = HOTELORDER.STATUSID) as Status from Hotelorder.
- In an application, this table is often used as a data source for DropDownList or other list controls.
- The table almost never changes.
2. Arrays and their problems
Realizing that there is a problem with this design, we will try to solve it now. The first way we can think of is to create an array in the program to represent the reservation state, so that we can delete the Bookingstatus state table (Note that this can be done because the contents of the Bookingstatus table are determined almost never changed).
String[] Bookingstatus = {
"Nouse", "uncommitted", "submitted", "cancelled", "accepted", "returned", "booked", "Expired"
}; Note that the number No. 0 element of the array is only a placeholder to make the program concise. Since Statusid starting from 1.
Let's see what it solves: The above mentioned problem 1, problem 2 is resolved, neither need to create a table in the database, do not have to connect to the database for query.
Let's take a look at what to do when we want to use text to display the hotel's reservation (assuming there is an order class Hotelorder, whose attribute Statusid represents the order status, the int type).
GetItem is used to obtain a hotel order object, OrderID is of type int and represents the ID of the order
Hotelorder MyOrder = GetItem (orderId);
Lbstatus.text = Bookingstatus[myorder.statusid]; Lbstatus is a Label control
So far it looks good, now we need to do an operation to change the status of the order to "accept".
Myorder.statusid = 4;
Unfortunately, we found the first problem with arrays: inconvenient use, when we need to update the status value of an order, we need to look at the definition of the Bookingstatus array (unless you remember the numeric values of all States), It then assigns values to the properties of the object based on the position of the state value in the array.
Let's look at another operation, if the status of an order is "expired," it should be deleted:
if (bookingstatus[myorder.statusid]== "expired") {
DeleteItem (MyOrder); Delete Order
}
The problem at this point is similar to the above: we need to manually enter the string "Expired", at this time Vs2005 smart hints do not play any role, if we unfortunately put the state value wrong, or hand mistakenly wrong, will lead to program errors, The more secure approach is to press F12 to the definition of the Bookingstatus array, and then copy "expired".
Now, let's look at how to bind to a DropDownList drop-down list control (ID ddlstatus).
Ddlstatus.datasource = Bookingstatus;
Ddlstatus.databind ();
But we found that the resulting HTML code was this:
<select name= "Ddlstatus" id= "Ddlstatus" >
<option value= "UNCOMMITTED" > not submitted </option>
<option value= "submitted" > Submitted </option>
<option value= "Canceled" > Canceled </option>
<option value= "Accept in" > Accept in </option>
<option value= "returned" > Returned </option>
<option value= "has been booked" > has been completed </option>
<option value= "Expired" > Expired </option>
</select>
We see that the value of the list item is the same as the text value, which is obviously not what we want to do. We can write a data-binding event-handling method to the drop-down list.
protected void Page_Load (object sender, EventArgs e) {
Ddlstatus.datasource = Bookingstatus;
Ddlstatus.databound + = new EventHandler (ddlstatus_databound);
Ddlstatus.databind ();
}
void Ddlstatus_databound (object sender, EventArgs e) {
int i = 0;
ListControl list = (ListControl) sender; Note that converting sender to ListControl
foreach (ListItem item in list. Items) {
i++;
Item. Value = i.ToString ();
}
}
In this way, we use the array to accomplish the effect we expect, although this implementation seems a bit cumbersome, although there are the above mentioned inconvenience to use the problem, but these problems we can be more patient and careful to overcome, and software development almost never has 100% perfect solution, then we simply good.
Note: In the Ddlstatus_databound event, sender of the object that raised the event is obviously DropDownList, but there is no conversion of sender to DropDownList, Instead, it is converted to the base type ListControl. This is done for better code reuse, and the Ddlstatus_databound event handling method will not be limited to DropDownList, for other controls that inherit from ListControl, such as RadioButtonList, The listbox can also use the Ddlstatus_databound method without modification.
If you are unfamiliar with event binding, refer to the delegation and events article in C #.
This can also be done using dictionary<string, int>, but there are similar problems, no longer an example.
3. Enumerations and their problems
But the unfortunate thing happened again ... Our booking process is divided into two parts: part of the B/s end, in the B/s end can be the establishment of the hotel order (not submitted), submitted (submitted), cancel the submission (cancelled), but also can see whether it has been completed, part of the C/s terminal, for the hotel's reservation center, it can do other state operations
At this point, there should be a total of 7 states for the system as a whole. But for the B/s end, it only has not submitted, has been submitted, has been canceled, has been booked four states, the corresponding values are 1, 2, 3, 6.
Let's recall how the array was used to solve the problem, which has a flaw: we default to the order status value corresponding to the array's index one by one.
Therefore, when binding DropDownList, we use the self-increment method to set the value of the list item, or in the display state, we pass lbstatus.text = Bookingstatus[myorder.statusid]; Such a statement to complete. When this correspondence is broken, the method of using the array is invalidated, because without the use of an array index, we have no additional place to store the numeric value of the state.
At this point, we thought about using enumerations:
public enum Bookingstatus {
Uncommitted = 1,
has been submitted,
has been canceled,
Already booked = 6
}
When we want to output the status of an order on the page, we can:
Hotelorder MyOrder = GetItem (orderId); Get an Order Object
Lbstatus.text = ((bookingstatus) myorder.statusid). ToString (); Output text value
We want to update the status of the order to "submitted":
Myorder.statusid = (int) Bookingstatus. has been submitted;
When the status is canceled, we want to perform an action:
if (bookingstatus. = = (Bookingstatus) myorder.statusid) {
Do some action
}
At this point, the smart hints of VS 2005 can already play a full role when we press "." After Bookingstatus. , all of the status values can be displayed.
Note: When we use the enumeration store state, the statusid of the Myorder object is best for the Bookingstatus enumeration type, not the int type, which is more convenient, but in order to be consistent with the previous use of the array, Here Statusid still uses the int type.
The above three scenarios use enumerations to be very fluent until we need to bind the enumeration to the DropDownList drop-down list: We know that there are two types of objects that can be bound to a drop-down list, such as an enumerable set that implements the IEnumerable interface, such as ArrayList, string[],list<t>; class is the realization of ilistsource data sources, such as Datatable,dataset.
Note: The GetList () method of the IListSource interface actually returns an IList interface, and the IList interface inherits the IEnumerable interface. In this view, IEnumerable is the basis for implementing an enumerable collection, which is discussed in detail in an enumerator in my translated article C #.
But we all know: Enum enum is a basic type, it does not implement any interface, then we come down how to do it?
4. Iterating through an enumeration field using reflection
The stupidest is also the simplest way, we can first create a getdatatable method, this method builds a DataTable based on the enumerated field values and numeric values, and finally returns the built DataTable:
private static DataTable getdatatable () {
DataTable table = new DataTable ();
Table. Columns.Add ("Name", Type.GetType ("System.String")); Create columns
Table. Columns.Add ("Value", Type.GetType ("System.Int32")); Create columns
DataRow row = table. NewRow ();
Row[0] = Bookingstatus. Not committed. ToString ();
ROW[1] = 1;
Table. Rows.Add (row);
row = table. NewRow ();
Row[0] = Bookingstatus. Submitted. ToString ();
ROW[1] = 2;
Table. Rows.Add (row);
row = table. NewRow ();
Row[0] = Bookingstatus. Canceled. ToString ();
ROW[1] = 3;
Table. Rows.Add (row);
row = table. NewRow ();
Row[0] = Bookingstatus. Already booked. ToString ();
ROW[1] = 6;
Table. Rows.Add (row);
return table;
}
Next, for ease of use, let's create a method that specifically uses this DataTable to set the list control Setlistcountrol ():
Set list
public static void Setlistcontrol (ListControl list) {
List. DataSource = Getdatatable (); Get a DataTable
List. DataTextField = "Name";
List. DataValueField = "Value";
List. DataBind ();
}
Now we can bind the enumeration to the list control in the page like this:
protected void Page_Load (object sender, EventArgs e)
{
Setlistcontrol (Ddlstatus); Assume that the page already has an ID of Ddlstatus DropDownList
}
If all the enumerations are bound to the list by this way, I think it might be better to build the table directly in the database, which is too cumbersome, and we hardcoding out a DataTable based on the enumerated text and values:
DataRow row = table. NewRow ();
Row[0] = Bookingstatus. Not committed. ToString ();
ROW[1] = 1;
Table. Rows.Add (row);
row = table. NewRow ();
Row[0] = Bookingstatus. Submitted. ToString ();
ROW[1] = 2;
Table. Rows.Add (row);
row = table. NewRow ();
Row[0] = Bookingstatus. Canceled. ToString ();
ROW[1] = 3;
Table. Rows.Add (row);
row = table. NewRow ();
Row[0] = Bookingstatus. Already booked. ToString ();
ROW[1] = 6;
Table. Rows.Add (row);
At this time, do we want to have a way to achieve this through traversal? If you want to traverse here, first, we need an object containing the information of each field of the enumeration, which contains at least two messages, one is the text of the field (such as "uncommitted"), and the other is the numeric value of the field (for example, 1), we call this object field. Second, there should be a traversal of the set of objects that contain field information (that is, filed), which we call the EnumFields.
So, the above can be implemented as follows:
foreach (xxxx field in EnumFields)
{
DataRow row = table. NewRow ();
Row[0] = field. Name; A fictional property that represents a literal value (such as "uncommitted")
ROW[1] = Filed.intvalue; A fabricated property that represents a numeric value (for example, 1)
Table. Rows.Add (row);
}
This code is very incomplete, we notice that XXXX, it should be the type of object that encapsulates the field information (or called metadata metadata). For EnumFields, its type should be the set of the XXXX type. This code is hypothetical and deduced according to our thinking. In fact,. Net provides the type class and the System.Reflection namespace to help solve our current problem.
I'll cover the type class in more detail later, and now I just want you to have a first impression of the reflection, so just make it simple: the typeabstract class provides the ability to access the type metadata, and when you instantiate a type object, you can get the type's metadata information through its properties and methods, or further obtain the metadata for members of that type. notice here that because the type object is always based on a type, and it is an abstract class, we must provide the type, or the instance of the type, or the string value of the type (part.2) when creating the type.
There are many ways to create a type object, in this case we use the TypeOf operator and pass the Bookingstatus enumeration:
Type enumtype = typeof (Bookingstatus);
Then we should find a way to get a collection of objects that encapsulate the field information. The type class provides the GetFields () method to implement this procedure, which returns a fieldinfo[] array. In fact, that's the type of enumfields we set up above.
fieldinfo[] EnumFields = Enumtype.getfields ();
Now, we can traverse this collection:
foreach (FieldInfo field in EnumFields)
{
if (!field. Isspecialname)
{
DataRow row = table. NewRow ();
Row[0] = field. Name; Get field Text value
ROW[1] = Convert.ToInt32 (Myfield.getrawconstantvalue ()); Get int value
Table. Rows.Add (row);
}
}
Here field's Name property gets the text of the enumeration, and the Getrawconstantvalue () method gets its value of type int.
Let's take a look at the complete code:
private static DataTable getdatatable () {
Type enumtype = typeof (Bookingstatus); Create type
fieldinfo[] EnumFields = Enumtype.getfields (); Get a collection of field information objects
DataTable table = new DataTable ();
Table. Columns.Add ("Name", Type.GetType ("System.String"));
Table. Columns.Add ("Value", Type.GetType ("System.Int32"));
Iterating through the collection
foreach (FieldInfo field in EnumFields) {
if (!field. Isspecialname) {
DataRow row = table. NewRow ();
Row[0] = field. Name;
ROW[1] = convert.toint32 (field. Getrawconstantvalue ());
ROW[1] = (int) enum.parse (enumtype, field. Name); You can do that.
Table. Rows.Add (row);
}
}
return table;
}
Note that the Setlistcontrol () method is still present and effective, just to save space, I didn't copy it, it's used the same as before, we just modified the getdatatable () method.
5. Using generics to achieve code reuse
Looking at the code above, if we now have another enumeration called Ticketstatus, then we will bind it to the list, and the only thing we need to change is here:
Type enumtype = typeof (Bookingstatus); Change Bookingstatus to Ticketstatus
In that case, why don't we define a generic class for code reuse? We call this generic class called enummanager<tenum>.
public static Class Enummanager<tenum>
{
private static DataTable getdatatable ()
{
Type enumtype = typeof (Tenum); Get type Object
fieldinfo[] EnumFields = Enumtype.getfields ();
DataTable table = new DataTable ();
Table. Columns.Add ("Name", Type.GetType ("System.String"));
Table. Columns.Add ("Value", Type.GetType ("System.Int32"));
Iterating through the collection
foreach (FieldInfo field in EnumFields)
{
if (!field. Isspecialname)
{
DataRow row = table. NewRow ();
Row[0] = field. Name;
ROW[1] = convert.toint32 (field. Getrawconstantvalue ());
ROW[1] = (int) enum.parse (enumtype, field. Name); You can do that.
Table. Rows.Add (row);
}
}
return table;
}
public static void Setlistcontrol (ListControl list)
{
List. DataSource = Getdatatable ();
List. DataTextField = "Name";
List. DataValueField = "Value";
List. DataBind ();
}
}
OK, now everything is much easier, and later, we need to bind the enumeration to the list, as long as this is OK (the DDL begins with DROPDOWNLIST,RBL, which begins with RadioButtonList):
Enummanager<bookingstauts>. Setlistcontrol (Ddlbookingstatus);
Enummanager<ticketstatus>. Setlistcontrol (Rblticketstatus);
Note: If you are unfamiliar with generics, see the generics article in C #. The above implementation does not take into account the performance issue, just to elicit an instance of reflection usage.
An example of reflection in 6. Net.
The reflection function is used both for VS2005 smart hints, or for refactoring functions when modifying variable names. Reflected shadows are often seen in the. Net FCL, which shows you one of the most common examples. As you know, there are two types in the CLR, one is a value type and one is a reference type. Declaring a variable of a reference type and instantiating the type, allocates memory on the application heap (application heap), creates an object instance, and then returns the memory address of the object instance to the variable, where the variable holds the memory address, which is actually equivalent to a pointer; Declares an instance variable of a value type. It is assigned to the thread stack, which itself contains all the fields of the value type.
Now suppose we need to compare the equality of two objects. When we compare the equality of variables of two reference types, we compare whether the two variables point to the same instance on the heap (the memory address is the same). And when we compare two value-type variables for equality, what do we do? Because the variable itself contains all the fields (data) of the value type, in comparison, it is necessary to compare the fields of two variables one-by-two, to see if the values of each field are equal, and to return FALSE if the values of any one field are unequal.
In fact, implementing such a comparison does not require us to write our own code, and Microsoft has provided us with a way to implement it: All value types inherit from System.ValueType, ValueType and all types inherit from System.Object, Object provides a equals () method that is used to determine whether two objects are equal. But ValueType overrides the Equals () method of object. When we compare two value type variables for equality, you can call the Equals () method that inherits from the ValueType type.
public struct Valpoint {
public int x;
public int y;
}
static void Main (string[] args) {
BOOL result;
Valpoint A1;
a1.x = A1.Y = 3;
Valpoint B1 = A1; Copy the value of A to B
result = A1. Equals (B1);
Console.WriteLine (result); Output True;
}
Do you have any idea what happens when you call the Equals () method? As we mentioned earlier, if it is a value type, the fields of the two variables are compared one after the other to see if the values of each field are equal, but how do we get all the fields of the variables, traverse the fields, and compare each of them individually? At this point, you should realize that it's time to use reflection again, let's use reflector to see the Equals () method of the ValueType class and see how Microsoft Does it:
public override bool Equals (object obj) {
if (obj = = null) {
return false;
}
RuntimeType type = (RuntimeType) base. GetType ();
RuntimeType type2 = (runtimetype) obj. GetType ();
if (type2! = type) {
return false;
}
Object A = this;
if (Cancomparebits (this)) {
Return Fastequalscheck (A, obj);
}
Get all entity fields
fieldinfo[] fields = type. GetFields (BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);
Traverse fields to determine whether field values are equal
for (int i = 0; i < fields. Length; i++) {
Object obj3 = ((rtfieldinfo) fields[i]). Internalgetvalue (A, false);
Object Obj4 = ((rtfieldinfo) fields[i]). Internalgetvalue (obj, false);
if (obj3 = = null) {
if (obj4! = null) {
return false;
}
} else if (!obj3. Equals (OBJ4)) {
return false;
}
}
return true;
}
Notice the two pieces of code that are annotated above, and you can see that when you compare a value variable, it is implemented using reflection. There is a poor performance problem with reflection (not only that, there are a lot of boxing operations), so it can be seen that the cost of calling the Equals () method on a value type is significant. But this example just to illustrate the use of reflection, I think has achieved the purpose. The above code does not fully understand and does not matter, will be mentioned later.
7. Summary
See here, you should have a preliminary concept of reflection (or a use of reflection): Reflection is a broad term, it is provided by the System.Reflection namespace and with the System.Type class, at runtime (runtime) for The basic information about types and objects (and their members) and the access capabilities of the metadata (metadata).
Reflection in
. Net (Prologue)-Part.1