VB.net can implement many functions that C # cannot do, such as the when statement, optional parameter, local static variable, static method for accessing the object instance, handles binding event, on error handling exception, and direct post-object binding. Both VB and C # belong to the same. NET language, and the same template is compiled. But why does VB support many interesting features. Let's explore it together.
(1) local static variables
VB supports the use of static keywords to declare local variables, so that the value of the variable can be maintained at the end of the process:
Public sub test1 ()
Static I as integer
I + = 1' implement a process call counter
End sub
We have implemented a simple process counter. Each time a test is called, the counter value increases by 1. In fact, there are many situations where we want to keep the value of the variable. The static state of C # cannot be used inside the process. Therefore, to implement the process counter, we must declare a class-level variable. This is obviously not as good as VB. Because it cannot prevent other procedures from modifying counter variables. This is similar to object encapsulation. It should have been a local variable of a method. Now I want to be forced to separate it. It is obviously not a good design. So how does VB generate local static variables? ReplaceCodeIn the back-to-assembly process, we can clearly see that in the WordPress generated by VB, I is not a local variable, but a field of the class:
. Field private specialname int32 $ static $ test1 $2001 $ I
That is to say, I is renamed as a class field, but it is named specialname. It is impossible to try to access $ static $ test1 $2001 $ I in code because it is not a valid identifier. However, in Il, the code that adds this variable to the code is exactly the same as that of a common class field, which is loaded through ldw.gov.cn. I think this method is very clever. It turns static variables into class fields with the same lifecycle, but the compiler controls the access permission and makes it a local variable. It also explains why VB uses two different keywords to declare static variables-static and shared.
Because local static variables are actually class fields, they are different from real local variables. For example, in multi-thread conditions, access to local static variables is the same as access fields.
(2) myclass keywords
VB.net supports a very interesting feature-myclass. Most people use myclass only when calling other constructors of this class. In fact, myclass can produce some very unique usage. Myclass is always called Based on the State in which the class member is not rewritable. That is, when the class method is overwritten, myclass can still get its own version. The following example is very similar to the example in VB help.
Public class myclassbase
Protected overridable sub greeting ()
Console. writeline ("Hello form base ")
End sub
Public sub useme ()
Me. Greeting ()
End sub
Public sub usemyclass ()
Myclass. Greeting ()
End sub
End Class
Public class myclasssub
Inherits myclassbase
Protected overrides sub greeting ()
Console. writeline ("Hello form Sub ")
End sub
End Class
We use a piece of code to test:
Dim o as myclassbase = new myclasssub ()
O. useme ()
O. usemyclass ()
The result is that useme executes the subclass version, while usemyclass executes the base class version, even though this is a virtual method. View its Il to see its simple implementation principle:
The Call Command Used by me is callvirt.
Il_0001: callvirt instance void app1.myclassbase: greeting ()
While myclass calls call
Il_0001: Call instance void app1.myclassbase: greeting ()
The strange thing is that I cannot implement such a simple function in C #. C # does not allow me to call a virtual function in non-virtual function mode. C ++ can use the Class Name: method name to access functions of its own version, but the class name of C # can only be used to access static members. This is really a strange limitation of C.
(3) handles and withevents
In addition to using the C # method to handle event responses, VB also has a unique event processing method inherited from vb5-withevents.
I like to call this event processing method static event processing. When writing an event response method, it is determined which event the method responds, C # binds events in the code. For example, the simplest example is as follows:
Public class handlerclass
Public withevents myobj as eventclass
Private sub myobj_myevent (byval sender as object, byval e as system. eventargs) handles myobj. myevent
Msgbox ("hello ")
End sub
Public sub new ()
Myobj = new eventclass
End sub
End Class
The eventclass used in the Code is as follows:
Public class eventclass
Public event myevent as eventhandler
Protected overridable sub onmyevent (byval e as eventargs)
Raiseevent myevent (Me, E)
End sub
Public sub test ()
Onmyevent (New eventargs)
End sub
End Class
Let's review it. This Code implicitly writes two methods for eventclass: add_myevent (eventhandler) and remove_myevent (eventhandler ), in fact, any event context is bound and unbound by calling these two methods. C # allows you to write your own event binding/unbinding code.
So how does withevents work? The VB.net compiler automatically changes
Public withevents myobj as eventclass
The process is as follows:
Private _ myobj as eventclass
Public property myobj () as eventclass
Get
Return _ myobj
End get
Set (byval value as eventclass)
If not (me. _ myobj is nothing) then
Removehandler _ myobj. myevent, addressof myobj_myevent
End if
Me. _ myobj = Value
If me. _ myobj is nothing then exit Property
Addhandler _ myobj. myevent, addressof myobj_myevent
End set
End Property
It can be seen that when the withevents variable is assigned a value, this attribute is automatically triggered to bind the event. Most of the event responses we use are 1-to-1, that is, a process responds to an event. Therefore, this withevents static method is very useful and can significantly enhance code readability, at the same time, it makes event processing in VB.net very convenient. Unlike C #, events must be manually bound without the Form Designer.
However, during the analysis of this section of Il, I also found that VB.net encountered a small problem in translation, that is, ldarg.0 appeared too much, which showed frequent use of me or this, therefore, we must note in the encoding process that, apart from using me/This itself for reference, do not include me/This when using its members, such as me. replace Myint = 1 with Myint = 1. This small habit will bring you great performance gains.
(4) type conversion Operators
In Visual Basic 2005, a new operator -- trycast is added, which is equivalent to the as operator of C. I always wanted VB to have such an operator. Currently, the types conversion operators of VB mainly include ctype and directcast. Their usage is almost the same. I have compared these two operators in detail and come to the following conclusion:
1. There is no difference between the two when converting to a reference type. The castclass command is called directly unless the type conversion operator ctype is overloaded. The directcast operator cannot be overloaded.
2. When converting to a value type, ctype calls the type conversion function specified by VB (if any). For example, if string is converted to int32, VisualBasic is automatically called. compilerservices. integertype. fromstring. to convert an object to int32, fromobject is called. When other numeric types are converted to int32, The ctype also calls the conversion method of the type to implement the conversion. The directcast operator is simple. It directly splits the object into the desired type.
Therefore, ctype is not faster than directcast but can support more conversions when used for value types. In C #, the type conversion is the (type) operator and the as operator. The (type) operator works in a similar way to VB's directcast, and is also directly split or castclass. However, if the supported type conversion (such as long to int) occurs, (type) the corresponding conversion method is also called, but the conversion from string to int is not supported. C # Another operator, as, is more intelligent. It determines whether the running instance of an object can be converted to the target type. Then, it can omit the castclass command and directly perform operations based on known types, in addition, the compiler can automatically optimize the as, for example, saving an object reference. Therefore, when converting an object to a required type, as is the best choice.
Because of the many advantages of As, Visual Basic 2005 absorbs this feature, you can use the trycast operator to get the same effect as, and the syntax is the same as that of directcast or ctype.
(5) Implementation Interface
The implementation interface syntax used by VB.net is the implements invented by vb5, which is unique in today's mainstream languages. For example, I have two interfaces:
InterfaceInterface1
Sub test ()
End Interface
InterfaceInterface2
Sub test ()
End Interface
The two interfaces have the same member test. What if I want to use a class to implement two interfaces at the same time? First, let's look at it. For Java, jscrip. NET and other languages, only one test function can be used to implement the test members of two interfaces. If the two test files are accidentally named again, what should we do if their content must be implemented separately? So some designs that solve the problem of duplicate names of interfaces ....... In VB, the unique implements statement allows you to implement the interface as needed. For example, the following implementation class implements two interfaces using two methods with different names.
Public class implementation
ImplementsInterface1,Interface2
Public sub Hello () ImplementsInterface1. Test
End sub
Private sub HI () ImplementsInterface2. Test
End sub
End Class
That is to say, VB allows functions of any name to implement members in the interface, and the accessors can be arbitrary, such as public or private.
C # the explicit implementation (explicit implementation) syntax is provided for processing duplicate members. The syntax for implementing the above two interfaces is
Public classClass1:Interface1,Interface2
{
Public class1 ()
{
}
VoidInterface1. Test ()
{
}
VoidInterface2. Test ()
{
}
}
Note: Here, C # can only use the interface name. member name to name the implementation method, and the accesser can only be private, not explicitly implemented.
After studying Il, I found that. Net supports implicit implementation and explicit implementation. Implicit implementation only requires a method with the same name as the interface member method in the class-This VB does not support. Explicit implementation adds the following to the description of the method:
. Override testapp. interface1: Test
This principle applies to both the explicit implementation of C # And the implements Statement of VB. That is to say,. NET provides the function of implementing interface members by name, but only VB gives this freedom to the user, while other languages still adopt the classic syntax.
(6) default attributes and attribute Parameters
In the original VB6, there was a strange function-default attribute. In VB6, the object name can directly represent the default attribute of the object. For example, the default attribute of textbox is text, so the following code
Text1.text = "hello"
It can be simplified
Text1 = "hello"
This simplification brings a lot of trouble to VB. The assignment operation requires two keywords: Let and set. The result attribute process also requires let and set. In addition, this feature can still work in later binding. To VB. NET, this function is greatly restricted. Currently, only attributes with parameters can be used as default attributes. For example
List1.item (0) = "hello"
Can be simplified
List1 (0) = "hello"
This syntax makes objects with default properties look like an array. So how does VB determine whether a property is a default property? See the following code
Public class proptest
Public property p1 (byval index as integer) as string
Get
End get
Set (byval value as string)
End set
End Property
Default public property P2 (byval index as integer) as string
Get
End get
Set (byval value as string)
End set
End Property
End Class
The P1 and P2 attributes are basically identical. The only difference is that P2 carries a default modifier. After disassembling this class, we can find that the two attributes are identical without any difference. However, the proptest class is added with a custom metadata attribute system. reflection. defaultmemberattribute. The member specified by this meta attribute is the default type used by invokemember, which means that the default attribute can be used for later binding. However, I tried to manually add the defaultmember meta attribute to the type, but it cannot be used to make a property a default attribute. It seems that this function is a "sweet Syntax" of VB ". However, the VB or C # compiler can only judge the default attributes of the classes generated by others by defaultmemberattribute. Therefore, I only use defamemmemberattribute to specify a default method for a VB class, instead of default, then compile it and use it for C #. Sure enough, C # recognizes it as an indexer )!
Now that we have talked about the C # indexer, let's take a look at the differences between VB and C # attributes. In the experiment just now, the default attribute of VB is the indexer in C. However, VB can still access the default attributes using the attribute syntax, while C # can only access the indexer using the array syntax. More specifically, VB can create properties that are not default, but contain parameters, such as P1 in the preceding example. C # does not support properties with parameters, if the class written in VB contains a parameter attribute for C #, C # will prompt "the attribute is not supported by this language. Please use the get_xxx and set_xxx syntax to access it ". That is to say, attributes with parameters are a function of CLR, but they do not comply with Cls (general language specifications). Therefore, cross-language barriers may occur. This also deepens our understanding of CLS-if you want your code to work across languages, be sure to comply with Cls.
(7) optional parameters and passing by name
VB supports the "optional parameter" feature from 4.0. That is, the function or sub-ProgramSome of the parameters are optional and can be left empty during the call. In fact, VB has some functions with optional parameters since 1.0, but only 4.0 allows users to develop such a process on their own. In vb4, optional parameters do not have default values. In VB. NET, optional parameters must have default values. For example
Public sub testoptional (optional I as integer = 1)
End sub
When calling, you can either write testoptional (2) or testoptional (). In this case, the parameter I is automatically equal to 1. If there is more than one optional parameter in the process, VB also provides a simplified method-passing parameters by name. For example, Process
Public sub testoptional (optional I as int32 = 1, optional J as int32 = 1, optional K as int32 = 1)
End sub
If you only want to specify K and let I and j use the default values, you can use the pass by name, as shown below:
Testoptional (K: = 2)
This method is not restricted by the order of parameter tables.
Testoptional (K: = 2, I: = 3, J: = 5)
These functions are indeed quite convenient, and C # does not support the above two features. Let's see how it is implemented at the Il level. The first method in Il is defined
. Method public instance void testoptional ([opt] int32 I) di-managed
{
. Param [1] = int32 (0x00000001)
. Maxstack 8
The [opt] modifier is added to the parameter, and. Param specifies the default value of the parameter. This is only the content that VB can recognize. C # will skip them. When calling, if VB finds that the parameter is omitted, the default value of the. Param part is automatically read and passed to the process explicitly. This part is completely handled by the compiler without any performance loss. It is exactly the same as passing all parameters manually. As for passing by name, VB will automatically adjust the order of parameters, and its results are no different from those transmitted in traditional methods. This means we can safely use this convenience. In addition, the process of obtaining C # With optional parameters changes to not-selectable parameters at most, which will not cause any other troubles.
PS. many COM components use default parameters, and the parameter list of some processes is very long. In VB, you can easily process them. in C #, developers are often allowed to transmit parameters to vomit blood.
(8) On Error and when statements
The exception handling statement is discussed here. In VB. NET, try... end try block is recommended for structured exception handling. However, to ensure compatibility, it also draws on the on error statement from earlier basic versions. In fact, on error is not the advantage of VB, because using it will destroy the program structure, making it difficult for programs with exception handling to understand and debug. However, I have always been amazed at how VB engineers implement it, because on error can make abnormal jumps flexible, unlike try. First, let's take a look at how try is implemented:
Public Function F1 () as integer
Try
Dim N as integer = 2 \ n
Catch ex as exception
Msgbox (ex. Message)
End try
End Function
This is the simplest exception handling program. Through reflector disassembly (if ildasm is used, do not select "Expand try-catch"), you can find that the entire process is translated19Command. Pay attention to this sentence:
. TryL_0000ToL_0006CatchException l_0006ToL_0022
This is a typical try block. It specifies the exception to be caught directly in the catch, and then specifies the position of the catch area, which is very clear. Pay attention to the following two sentences:
L_0007: Call projectdata. setprojecterror
L_001b: Call projectdata. clearprojecterror
It can be seen that these two sentences are at the beginning and end of the Catch Block. Going deep into these two processes, I found that it is recording exceptions for the ERR Object. It seems that the use of err is also a sweet syntax, and the performance is painstaking. These two statements are added out of thin air (Fortunately, they are not very complex ).
Next I wrote a function similar to this function, using the on statement to handle exceptions:
Public Function F2 () as integer
On Error goto catchblock
Dim N as integer = 2 \ n
Exit Function
Catchblock:
Msgbox (ERR. description)
End Function
This is not complicated than the previous process, but after disassembly, its il Code actually has47Command lines. 19 commands just now! The main change is the try part. Now it is like this:
. TryL_0000ToL_0022FilterL_0022 l_0036ToL_0060
Note: The catch is lost, but the filter is displayed. I have never seen filter in the Il generated by C. I have queried the meta data document. The filter can be used to filter the blocks that meet certain conditions to process exceptions. In this example, the l_0022 command is used as a filter, it is:
L_0022: isinst exception
L_0027: brfalse. s l_0033
L_0029: ldloc. s V_4
L_002b: brfalse. s l_0033
L_002d: ldloc.3
L_002e: brtrue. s l_0033
L_0030: LDC. i4.1
L_0031: Br. s l_0034
L_0033: LDC. i4.0
L_0034: endfilter
Endfilter is the beginning of some code for exception handling. The code before l0030 is part of the filter judgment, and V_4 and V_3 are the variables added by VB to save the error code. In the whole disassembly, I found that the Code designed to handle exceptions is actually in the try block in Il, that is to say, the program structure is no longer a regular try... catch Block, the statements that generate exceptions are combined with the statements that handle exceptions. The commands that actually handle exceptions are a lot of tedious jump statements.
The third example I wrote is as follows:
Public Function F3 () as integer
On Error resume next
Dim N as integer = 2 \ n
End Function
Two rows of this value use the powerful syntax killer of VB-on error resume next, which will ignore all exceptions and allow the code to continue executing the statements that generate exceptions immediately, how many il commands does this function generate? The answer is:50Items! It is longer than a common on error. I will not say much about its implementation, which is similar to the previous on statement. However, the number 50 seems to remind everyone not to use on error in the program to handle exceptions. The price is unacceptable.
The last example is the when Statement of VB. NET, which can filter the catch part:
Public Function F1 () as integer
Dim N as integer = 0
Try
Dim M as integer = 2 \ n
Catch ex as exceptionWhen n = 0
Msgbox (ex. Message)
End try
End Function
The when statement is used to determine the Variable N. It only enters the Processing Section when n = 0. When we hear the word "filter", we have guessed that it is implemented using try... filter. Yes. Here, the filter is mainly used to determine whether the ex type is exception, and whether N is equal to zero. When the filter is successful, it will be transferred to the exception processing segment for processing. The code generated by VB is much clearer than the on error statement rules.
This time, we also learned the try filter structure using the on error and when statements, which cannot be generated by C #. Therefore, I found that it cannot be decompiled by common decompilers (because the decompilers only know C #, haha ). In addition, after the on error is used, the program structure becomes abnormal and chaotic. In this case, is it possible to protect our code in disguise?
(9) instance access to shared members
We all know that static members are called shared members in VB. Although it is a bit awkward to accept, "shared members" are indeed worthy of the name:
Public class class1
Public shared I as integer
'Other none-shared members
End Class
In C #, you can use class1. I to access shared Member I, and use instance variables to access:
Dim C1 as new class1
C1. I = 100
Just like I is a member of C1! Of course, there is only one I, and any instance that modifies the I value will change all I values (because there is only one ). Even me and myclass can access Shared members.
Me. I = 100
Myclass. I = 100
This is not possible in C #. A pure C # programmer will feel incredible when seeing the code. To reveal how it works, we can do the following experiments:
Dim C1 as class1
C1. I = 100
Note that C1 here is nothing !, Even a nothing variable can access Shared members without errors. Next we will show more extreme situations in our experiment:
Dim o as object = new class1
O. I = 100
Result -- failed. The shared member cannot be accessed through later binding. Now the results are obvious. Only when VB explicitly understands the object type can the shared member be accessed by the instance. VB will automatically determine the type, then rewrite all the statements used to access Shared members
Class1. I = 100
This syntax. Delphi also supports this interesting feature, and Li Wei described it as one of the extensions of Delphi. Net to. Net in "Inside VCL.