Use C ++ in-depth research .NET's commission and events (transfer)

xiaoxiao2021-03-06  61

Written J. Daniel Smith Translation Zeng Yi Last Update: March 14, 2004: Declaration Published in "Dr.Dobb's Journal Software Development" magazine The best way to event is yourself to achieve them - use previous pure C . Introduction Type security mechanism is originally used as a Callback function, while .NET Framework introduces commission and events to replace the original way; they are widely used. We tried to use standard C to implement similar features so that we can not only have a better understanding of these concepts, but also experience some interesting technologies of C . The entrusted keywords in C # First Let's take a simple C # program (the code below is slightly deleted). The output of the execution program is displayed as follows: SimpleDelegateFunction Called from Ob1, String = Event Fried! Event Fired! (OB1): 3:49:46 PM On Friday, May 10, 2002 Event Fried! (OB1): 1056318417 SimpleDelegateFunction Called from OB2 String = Event Fried! Event Fried! (OB2): 3:49:46 PM ON Friday, May 10, 2002 Event Fried! (OB2): 1056318417 All of this is from this line: dae.fireprintstring ("Event Fired ! "); When using C to implement these functions, I imitate the syntax of the C # and developed according to the requirements of the function.

namespace DelegatesAndEvents {class DelegatesAndEvents {public delegate void PrintString (string s); public event PrintString MyPrintString; public void FirePrintString (string s) {if (MyPrintString = null!) MyPrintString (s);}} class TestDelegatesAndEvents {[STAThread] static void Main (string [] args) {DelegatesAndEvents dae = new DelegatesAndEvents (); MyDelegates d = new MyDelegates (); d.Name = "Ob1"; dae.MyPrintString = new DelegatesAndEvents.PrintString (d.SimpleDelegateFunction); //. .. more code similar to the // Above few lines ... dae.fireprintstring ("Event Fired!");}}}} Class MyDelegates {// ... "Name" Property Omitted ... Public Void SimpleDelegateFunction (String S {Console.WriteLine ("SimpleDelegateFunction Called from {0}, String = {1}", M_Name, S);} // ... more method in C type security function refers to "Vintage method "One of the criticisms is that they are not type security [1]. The following code proves this point of view: typedef size_t (* func) (const char *); Void Printsize (const char * str) {func f = strlen; (void) Printf ("% s% ld chars / n", STR, F (STR));} void crashandburn (const char * str) {func f = reinterpret_cast (strcat); f (str);} code can be found in [2]. Of course, when you use ReinterPret_cast, you may have trouble. If you will force the conversion (CAST), the C compiler will report an error, and more secure STATIC_CAST is not able to complete the conversion. This example is also a bit like apples and oranges, because everything in C # is object, and ReinterPret_cast is equivalent to a solution. The following C program example will take the method of using the member function pointer to avoid using ReinterPret_cast: struct object {}; struct str: public object {size_t len ​​(const char * str) {Return Strlen (STR);} char * cat (Char * S1, Const Char * S2) {RETURN STRCAT (S1, S2);} typedef size_t (turnc *); void printsize (const char * s) {str str; func f = STATIC_CAST

(& Str :: LEN); (void) Printf ("% s iS% ld charS / n", s, (str. * F) (s));} void crashandburn (const char * s) {str str; func f = static_cast; (str. * f) (s);} static_cast operator will transform the str :: LEN function pointer, because Str is derived from Object, but str :: Cat is type security It cannot be converted because the function signature is not matched. The work mechanism of the member function pointer is very similar to the conventional function pointer; the only difference (except for more complex syntax), you need an instance of a class that calls the member function. Of course, we can also use the -> * operator to complete the call to the member function with the pointer to the class instance. Str * pstr = new str (); func f = static_cast

(& Str :: LEN); (void) Printf ("% s% ld charS / n", s, (str -> * f) (s)); delete pstr; as long as all classes are derived from base classes Object Come (this is this in C #), you can use C to create a type of security member function pointer. Creating a delegate class with type security member function pointer is the first part we follow .NET functionality. Despite this, a separate member function pointer is unused - you always need an instance of a class; the delegate object is maintained at both sides, making the calling member function is very convenient. We followed the above example of writing the following code: struct StrLen_Delegate {typedef size_t (Str :: * MF_T) (const char *); MF_T m_method; Object & m_pTarget; StrLen_Delegate (Object & o, const MF_T & mf): m_pTarget (& o), MF_TMETHOD () const {return () const {return * m_pt {return * m_ptarget;} size_t invoke (const char * s) {(m_ptarget. * m_method) (s);}}; void PRINTSIZE2 (Const Char * S) {Str Str; Strlen_Delegate D (Str, & Str :: LEN); (Void) Printf ("% s IS% LD Chars / N", S, D.INVoke (s));} The delegate class, calling member functions become simpler. Use the operator instead of invoke to create an imitation function to make the call to only D (s); for clarity and the .NET specified, I use Invoke. It should be noted that an example of a class is an object (Object) instead of STR. As long as the signature matches, any of the members of any class that is derived from Object will allow to be used to create a delegate. This class can work very well in this example, but it is not very flexible; we must write a new entrustment class for every possible member function. .NET uses the RICH Type information maintained by the common language runtime to solve this problem. But this is not a very feasible approach in C , but you can use templates to complete similar functions. We don't have to set the parameters of the Invoke function to const char * s, but specify the type as a template parameter: Template

Struct strlen_dlegate {typedef size_t (str :: * mf_t) (arg1); // ... as Above ... size_t invoke (arg1 v1) {(m_ptarget. * m_method) (v1);}}; this is good There are many, but the Invoke function will only work on a single-parameter member function. Also, the delegation is only the example of the class and the member function pointer; it is not a truly concern of the member function pointer. Finally, we can easily generate a typedef as a template parameter for a member function pointer. Since everything is derived from the Object class, these details can also be moved to Object: struct object {Templatestruct void1_t {typedef void (object :: * mf_t) (arg1);}; template

Void Invoke (void1_t

:: MF_T MF, Arg1 V1, Arg2) Const {(this -> * mf) (V1);}}; Template

Class Objectt: Public Object {}; typedef objectt

VoidType; This Object base class contains a TypedEf corresponding to each member function signature; I used VoID return type to simplify a lot of work that needs to do. Typedef can be used as follows: typedef object :: void1_t

:: mf_t stringmf_t; We use the std :: string type parameter and VOID return type to create TypedEf for the member function pointer very easily. The program is tracked for invoke based on additional parameters. This is very necessary, because there must be the same number of parameters for all INVOKE methods; the overload decision is based on the first parameter - the type of member function pointer is completed. It should be noted that most of the .NET Framework will use Eventargs objects in the commission to avoid the above complexity. You can add additional parameters from Eventargs to add additional parameters without need to add a signature. Finally, the ObjectT template provides a simple method to generate a unique type, each type eventually derived from Object. This ensures type security. Based on all the content above, the entrustment class should now look like the following: Template

Class Delegatet_: public objecttt

{MF_T m_method; Object * m_pTarget; protected: DelegateT_ (): m_pTarget (NULL), m_method (NULL) {} DelegateT_ (Object & o, const MF_T & mf): m_pTarget (& o), m_method (mf) {} public: MF_T Method () Const {RETURN M_METHOD;} Object & target () const {return * m_ptarget;}}; template parameter is now a TypeDef member function pointer (shown above), and the invoke method is inherited in the Object base class. Maintenance commission set in C #, Delegate and Event keywords are used to create a list of delegates, just like the first example above: New delegateSandevents.printString (D.SIMPLEDELEGATEFUNCTION); Create a new similar to my C implementation Privilege object: Strlen_Delegate D (Str, & str :: LEN); MyPrintString object is an event with overloaded operators =, which is used to add delegates. In C , we can also imitate this feature to complete similar work. The Delegate keyword in the C # creates a multicastdeLegate object (see [3]). You will notice that I name the delegation class is delegatet_ (the latter underscore means this name is reserved). Strictly speaking, the name_delegatet is reserved for this program (__dlegatet is also the same) because of the next line, followed by a capital letter. _Delegatet can also (only one underscreen of lowercase letters), but I am biased to avoid all the potential errors due to the prior to the crowd (reading the code I have written, I can't catch all my rules. ) Is also reluctant to use the back line instead of it. Keep Delegatet_ is because the entrustment class of the completion of the .NET function is derived from the multicast delegate (MulticastDelegate) class. The Delegate object can be easily stored in a standard C container. I will use List because it is closest to .NET's work mechanism. According to your personal needs, you can also use Vector or Deque. Use the set (SET) to provide only a fun feature that is only invoked for a few times. The first part of MULTICASTDELEGATE is as follows: TemplateClass MulticastDelegatet: Public Delegatet_

{typedef delegatet_

Delegate; typedef st :: list

Delegates_t; protected: MulticastDelegateT () {} public: MulticastDelegateT (Object & o, const MF_T & mf): Delegate (o, mf) {} MulticastDelegateT & operator = (const Delegate & d) {m_delegates.push_back (d); return * this;} Private: delegates_t m_dlegates;}; here uses List and several typedef to store commission sets. It needs to be derived from Delegatet_, because I will derive Delegatet from MulticastDelegatet as a real commission class. Then, the events in all C # cycles on the commissioned commissioned commissioned and call each one. Because I am using a standard container, it will make it easy: void operator () (arg1 v1 = voidType (), arg2 v2 = voidType (), arg2 v2 = voidType () Const {for (delegates_t :: const_iterator it = m_dlegates.begin (); It! = m_dlegates.end (); IT) (it-> target ()). Invoke (it-> method (), v1, v2);} Even if you adapt to standard C containers, this may be you don't Familiar line of code: You can use iterators to call members functions only in a template class! For the iterator, we can clearly see what happened: const delegate & d = * it; d.invoke (D.Method (), V1, v2); if you are not very adaptable to iterators, you It can be pointed out in the same as an array: for (int i = 0; Ivoid Invoke_ (Arg1 V1 = arg1 (), arg2 v2 = arg2 ()) const {this-> invoke (m_method, v1, v2);} Avoiding the MulticastDelegatet :: Invoke method must pass the member function pointer to Object :: Invoke: D.invoke_ (V1, V2); despite this, this will need each parameter has a default constructor, but the facts are not seen This is true. And, because MulticastDelegatet is a real commission base class, it seems that there is not much necessary call Object :: Invoke path - even because this code is more complicated. (This will also cause it in Visual C . Terrible "internal compiler error"). The actual entrustment class is now just a simple packaging of MulticastDelegatet: Template

Struct Delegatet: Public MultiCastDelegatet

{DELEGATET (Object & O, Const MF_T & MF): MulticastDelegatet

(o, mf) {} delegatet () {} typedef delegatet

Event;}; its main function is to provide event typedef. I can integrate them now you can write the delegateSandevents class in the C to implement the C # example: class delegatesandevents {// c #: public delegate void printstring (string s); typedef delegatet

:: MF_T, Std :: String> PrintString_; public: TemplateStatic PrintString_ PrintString (Object & O, Void (Object :: * mf) (std :: string) {Return PrintString_ (O, STATIC_CAST

:: mf_t> (mf));} // C #: public evenet printstring myprintstring; printstring _ :: Event myprintstring; void fireprintstring (std :: string s) {myprintstring (s);}}; such syntax looks Horror, if you like, you can simplify it with some smart macro. But the name of the macro is not very good, and the key to this topic is to understand the details. No matter what, you should thank the C # compiler for your work. The first line of code creates a member function pointer private TypeDef, named PrintString_. Parameter type std :: string requires column twice, which is too bad, but this is due to Visual C does not support local template. The Static method provides a convenient way to create your own type, allow you to write your code: delegateSandevent :: PrintString_ mydelegate = delegateSandevents :: PrintString (D, & myDelegates :: SimpledeGateFunction); this with the top of C # The code is similar. Then, we created events from Event TypedEf from Delegatet_. Note how this series of typedef allows C code to be at least C # code something similar. Finally, there is a method triggering an event, which is especially the same as C #. (Because you use standard containers, you don't have to worry about NULL list.) The code for clients with delegates and events is very clear, and it is also very similar to C # code (the same these code is also slightly reduced): struct mydlegates: Public Objectt

{// ... name omitted ... Void SimpleDelegateFunction (std :: string s) {printf ("SimpleDelegateFunction Called from% S, String =% S / N", m_name.c_str (), s.c_str ()) } // ... more Methods ...}; voidatesndevents dae; mydlegates d; d.name () = "obj1"; dae.myprintstring = delegateSandevents :: PrintString (d, & mydelegates :: SimpledeLegateFunction; // ... more code Similar to the Above Few Lines ... dae.fireprintstring ("Event Fired!");} Please note that multicastdeLegatet :: Operator = is called to add each of the delegate list Static method delegatesandevents :: PrintString returned. Managed C is part of the .NET supported by the delegate and events, all .NET supported languages ​​can be used. The template-based implementation I have described is specifically for C . Microsoft uses different methods to disclose this feature in C - for standard C expansion is called managed C . Maybe you are not very surprising, writing this example in the hosted C is so similar to the original code: public __gc strunt delegatesAndevents {__event void myprintstring (string * s); void fireprintstring (String * s) {MyPrintString (s); }}; __gc struct MyDelegates {String * Name; void SimpleDelegateFunction (String * s) {Console :: WriteLine ( "SimpleDelegateFunction called from {0} string = {1}", Name, s);}}; void ManagedCpp () {DelegatesAndEvents * dae = new DelegatesAndEvents (); MyDelegates * d = new MyDelegates (); d-> Name = "Obj1"; __hook (& DelegatesAndEvents :: MyPrintString, dae, & MyDelegates :: SimpleDelegateFunction, d); dae-> FirePrintString ( S "Event Fired!");} Keyword __gc marks this class is controlled by the garbage recycling mechanism (managed); we don't need to call the Delete function. Just one __event keyword completes most of our above code. It should be noted that hosting C uses the __hook keyword to replace the operator = discussed above. You will find the -FX tag [4] call (managed) C compiler to compile the above code and check the result file. MRG is very interesting. Adding new features in the compiler level instead of writing a template is much easier.

Conclusion By using an extremely advanced C trick, I have already demonstrated the use of C as a simple sample code to achieve entrustments and events. This implementation mainly considers based on the .NET framework. More first-rate and pure C solutions can use adapters and widgets in the C standard library. Reference [1] Jeffrey Richter. "An Introduction To Delegates," MSDN Magazine, April 2001. . [2] Richard Grimes: Making Asynchronous Method Calls in the .NET Environment, "MSDN Magazine, August 2001 .. [3] Jeffrey Richter." Delegates, Part 2, "MSDN Magazine, June 2001. [4] Bobby Schmidt. "The Red Pill," April 23, 2002.

About the author J. Daniel Smith is a software engineer holding Autodesk certified by Michigan. He has obtained a bachelor's degree from the Galvin University and has obtained a master's degree in computer science in Michigan State University. You can get in touch with him through cuj@jdanielsmith.org. Translator Note: Translation 1: Type-Safe: Translated into "Type Safety" in accordance with the introduction of Microsoft officially provided in 2003. Translation 2: Overload Resolution: Translated as "overload decision" in accordance with the glossary of Microsoft in 2003. Translation 3: The address of the references listed in the original article has been invalid, and the translation is provided in the latest and effective links shown in the translation of the translation of the translation. Translation 4: The word DESTRUctor Translated as "reverse reference" in accordance with Simplified Chinese. Translation 5: Reinterpret_cast used in the text. In fact, ReinterPret_cast is here to pass. Because we can't make a so-called type conversion for the member function pointer. This example is actually in the comparison object, the conversion is also an object, not a member of the object. This example is a solution that is directly compared to the object of the object, and does not consider the object. That is, try to convert the member of the object. The true intent of the loss of type conversion. Why did the author use ReinterPret_cast_cast here, meaning "Forced Conversion in Refresh". This conversion is not based on type or object, and it is not more secure. Essentials in the entrusted are function pointers, but it needs to be checked first. We say that the existence of the entrusted object is just for the type check, it is really meaningful or its method. So ReinterPret_cast is equivalent to a solution.

转载请注明原文地址:https://www.9cbs.com/read-112145.html

New Post(0)