C # delegate Introduction
Stanley B. Lippman
[Interportion: This is an old article. However, there is no doubt that lippman is incorporated by Delegate. ]
If you want to compare C # with other "C family" language, C # is a unusual characteristic, which does not have a true meaning in C or Java.
C # is a controversial emerging language, which is created by Microsoft, as the cornerstone of Visual Studio.NET, is currently in the first BETA version of the release phase. C # combines many features derived from C and Java. Java community to C # primary criticism is that it claims that C # is just a java clone version - and it is said that it is the result of a litigation. In the C community, the main criticism (also targeting Java) is, C # is just another flooded private language (YET ANOTHER OVER-HYPED ProPrietary Language).
This purpose is to show a language characteristic of C #, and similar features are not directly supported in C or Java. This is the C # Delegate type, which operates approximates a pointer to the member function. I think that C # delegate is deeply thoughtful innovative language features, C programmers (no matter what idea to C # or Microsoft) should have special interests on this feature.
In order to stimulate discussions, I will be elaborated around a Testharness Class. This Testharness Class enables any category to register Static or Non-Static's Class Methods for subsequent execution. DELEGATE is the core of Testharness Class.
C # delegate Type
Delegate is a function pointer, but the main difference is three compared to ordinary function pointers:
1) A delegate Object can be equipped with multiple methods (Methods) [Method 1] instead of one. When we evoke a Delegate equipped with multiple methods, all methods were evokeed in turn - waiting for us to see how to do this.
2) A method of delegate Object (Methods) does not need to be the same category. All methods (Methods) equipped with Delegate Object must have the same prototypes and forms. However, these methods can be STATIC and Non-Static, can be composed of one or more different categories of members.
3) A declaration of Delegate Type is essentially a new Subtype Instance, which is derived from the .NET Library Framework's Abstract Bases Delegate or MulticastDelegate, which provides a set of public methods to inquire DELEGATE OBJECT or it is equipped Method (Methods)
Declaration delegate Type
A declaration of a delegate Type is generally consisting of four parts: (a) access level; (b) keyword delegate; (c) Return type, and declaration form of the method of Delegate Type; (D) delegate The name of Type is placed between the declaration form of the return type and method. For example, a public delegate Type action is declared, which is used to carry "there is no parameter and have a VOID return type": public delegate void action ();
At a glance, this is similar to the function of definition; the only difference is to DELEGATE keyword. The purpose of increasing this key is to distinguish between keywords - ratherd-than numerals - to distinguish between the common member functions from other forms of syntax. This has Virtual, Static, and Delegate used to distinguish the grammatical forms of various functions and form functions.
If a delegate Type is only equipped with a single method (Method), it can be equipped with any return type and form-based member function. However, if a Delegate Type is equipped with multiple methods at the same time, the return type must be Void [Translation 2]. For example, ACTION can be used to mount one or more methods (Method). In Testharness Class implementation, we will use the above Action declaration.
Delegate Handle
In C # we can't declare global objects; each object definition must be one of the three of the following: local objects; or other object members; or parameters in the function parameter list. Now I only show you the declaration of Delegate Type. After we will see how to declare it as a member of the category.
DELEGATE TYPE in C # is the same as Class, Interface, and Array Types, belonging to Reference Type. Each REFERENCE TYPE is divided into two parts:
A named handle (Named Handle), is manipulated directly; and a unable object of the type belonging in this handle, and is manipulated by the handle. This object must be created via NEW.
Define the Reference Type is a "two steps" process. When we write:
Action theAction;
At the time, theaction represents a Handle (handle) of "Delegate Type Action", which is not delegate object. By default, it is set to NULL. If we try to use it before it is assigned (the translation: Assigned, that is, the corresponding type of object is attachment), the compile period is used. For example, statement:
TheAction ();
Evil the method to be equipped (Method (s)). However, unless it is defined, it is not conditionally assigned (the translation: Assigned, which is an attachment with the corresponding object), otherwise the statement will trigger the compile period error and print the relevant information. Assign space for Delegate Object
In this section, in order to continue to explain with a minimum involvement, we need to access a static method and a non-static method (Non-Static Method), this I use an Announce Class. This category Announcedate static method (Static Method) prints the current date to standard output device in the form of long form (using a full single length of single words):
Monday, February 26, 2001
Non-Static Method AnnounceTime prints the current time to standard output devices in the form of Short Form (smaller representation):
00:58
The first two numbers represent an hour, start calculated from the time of midnight, and the last two numbers represent minutes. Announce Class uses the DateTime Class provided by the .NET Class Framework. The definition of the Announce category is as follows.
Public Class Announce
{
Public static void Announcedate ()
{
DateTime DT = datetime.now;
Console.WriteLine ("Today's Date IS {0}",
Dt.tolongDatestring ());
}
Public void AnnounceTime ()
{
DateTime DT = datetime.now;
Console.writeline ("The Current Time Now IS {0}",
Dt.toshostTimeString ());
}
}
To let theAction mount the above method, we must create an action delegate Type with a New expression (the object that creates a class). To carry a static method, the quotes of the incoming constructor consist of three parts: the name of the category belongs; the name of the method; DOT OPERATOR that is separated by two names (.):
TheAction = new action (announce.announcedate);
To be equipped with a non-static method, the quotes of the incoming constructor are also composed of three parts: the name object name belong to the method; the name of the method; DOT OPERATOR separated by two names (.):
Announce an = new announce ();
TheAction = new action (an.announcetime);
It can be noted that theAction is directly assigned, no check in advance (for example, check whether it has been referring to an object in a heap, if yes, first delete the object). In C #, the object existing in the Managed HEAP (hosted stack) is applied to garbage collection operations due to the operating period environment (Garbage Collected). We don't need to explicitly delete those objects allocated via NEW expression. In the managed heap of the program, the New expression can be allocated for an alone object.
Hellouser myprog = new hellouser ();
You can also assign array objects.
String [] Messages = new string [4];
The form of the assignment statement is: the name of the type, followed by the keyword NEW, followed by a pair of circular projections (represent a single object) or square bracket (represent array object) [1]. (A universal feature in the C # language design is to insist on using a single clear form to distinguish between different functions.)
A fast overview: Garbage Collection (garbage collection)
As shown in the following array objects, when we assign space for Reference Type in Managed Heap:
Int [] FIB = New INT [6] {1, 1, 2, 3, 5, 8};
Object Automatic Maintenance The number of handles (Handles). In this example, an associated reference counter is initialized to 1 by the array object pointing to the FIB. If we now initialize another handle, it points to the array object referred to in FIB:
int [] notfib = FIB;
This initialization causes a shallow copy (shallow copy) for the III group object referred to in FIB. That is to say, NOTFIB now also points to an array object pointing to the FIB. The reference count associated with the array object becomes 2.
If we modify an element in an array via NOTFIB, for example
NOTFIB [0] = 0;
This change is also visible to FIB. If this multi-access method for the same object is not required, we need to write code and make a Deep Copy (deep copy). E.g,
// Assign another array object
NOTFIB = New Int [6];
/ / Start from the 0th element of NOTFIB,
// copy the elements in the FIB to the Notfib sequentially.
// See the notes [2]
FIB.COPYTO (NOTFIB, 0);
NOTFIB now does not refer to the object referred to in FIB. It was previously subjected to the reference number of the associated reference count of two simultaneous points. The initial reference count of the object referred to in NOTFIB is 1. If we now also re-value the FIB as a new array object - for example, an array containing the first 12 values of the Fibonacci number:
FIB = New int [12] {1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144};
For the array object previously referred to by FIB, its current reference count turns 0. In the Managed Heap, when the garbage collector is active, the reference count is 0 is deleted.
Define Class Properties
Let us now declare a private static member of Testharness Class. For example [3], Public Class Testharnes
{
Public delegate void Action ();
Static private action;
// ...
}
Next we have to provide reading and writing mechanisms for this delegate member. In C #, we don't provide an explicit inline method (inline methods) to read and write non-public data members. Instead, we provide GET and SET Visits (ACCESSORS) for the name of the name. Here is a simple delegate property. We may wish to be called Tester:
Public Class Testharnes
{
Static Public Action Tester
{
Get {return.
SET {Action = VALUE;
}
// ...
}
Property can encapsulate static data members, or encapsulate non-static data members. Tester is a Static Property (Static Properties) of Delegate Type Action. (You can notice. We define Accessor as a code block. The compiler is included inside the compiler generated inline method.)
GET must be used as a model of Property as a return type. In this example, it returns directly to the encapsulated object. If "lazy allocation" is used, GET can be constructed and stored in the first time being raised, in order to use.
Similarly, if we want Property to support write access, we offer SET Accessor. Value in the set is a conditional keyword (CONDitional-Keyword). That is, Value has predefined meaning only in Set Property (the translation note: That is, Value is only seen as a key in the set code segment): it always represents "The Property" "Object. In our example, Value is an action of an action. During operation, it is bound to the right of assignment expressions. In the example below,
Announce an = new announce ();
Testharnes.tester =
New Testharness.Action
(An.announcetime);
SET is expanded from the inline (inline) where Tester appears. The Value object is set to an object returned by a New expression.
Evil DELEGATE OBJECT
If you see it, you have to evoke the method of delegate, we apply Call Operator to Delegate:
Testharness.tester ();
This sentence evokes the Tester Property GET Accessor; Get Accessor returns theAction Delegate Handle. If theaction does not point to a delegate Object at this moment, it will have an exception thrown. Decree actions from categories (delegate-tempered, first implementation agent, re-test, final implementation) as follows: IF (Testharness.Tester! = NULL)
Testharness.tester ();
For Testharnes Class, our method is only simple to package such a test:
Static Public Void Run ()
{
IF (theAction! = null)
TheAction ();
}
Connect multiple delegate Objects
To make a delegate equipped with multiple methods, we mainly use = Operator and - = Operator. For example, imagine that we define a TestHashTable Class. In the constructor, we add each associated test to Testharnes:
Public Class Testhashtable
{
Public void test0 ();
Public void test1 ();
Testhashtable ()
{
Testharness.tester = new testharness.action (TEST0);
Testharness.tester = new testharness.action (TEST1);
}
// ...
}
Similarly, if we define a TestarrayList Class, we also join the associated test in the Default Constructionor. It can be noted that these methods are static.
Public Class TestarrayList
{
Static public void testcapacity ();
Static public void testsearch ();
Static public void testsort ();
TestarrayList ()
{
Testharness.tester = new
Testharness.action (TestCapacity);
Testharness.tester = New Testharness.Artion (TestSearch);
Testharness.tester = new testharness.action (testsort);
}
// ...
}
When the Testharness.Run method is evoked, usually we don't know which one in TestHashTable and TestarrayList is evoked first; depending on the order of their constructor being evoked. But we can know that for each category, the order of its method is evoked is the order of the method being joined DELEGATE.
Delegate Objects and Garbage Collection (Garbage Collection)
Expect the code segment in the following local scope:
{
Announce an = new announce ();
Testharness.tester =
New Testharness.Action
(An.announcetime);
}
When we add a non-static method to the Delegate Object, the address, and "of this method are used to evoke the method," said Handle "that points to the category object. This causes the reference count associated with this category object to be automatically increased. After initialization via NEW expression, the reference count associated with the object in the Managed Heap is initialized to 1. When the AN is transmitted to the delegate Object constructor, the reference count of the Announce object is increased to 2. After walking out of the local role, the AN's survival ends, the reference count is reduced to 1 - Delegate Object also occupies one.
The good news is that if a delegate references a method of an object, it can be ensured that the object will be applied until "Delegate Object no longer references the method", it will be applied to garbage collection processing [4]. We don't have to worry that the object will be laid out under his eyes. The bad news is that the object will continue (the translation: this may be unnecessary) until the delegate Object does not reference its method. You can use-= Operator to remove the method from the Delegate Object. For example, the version of the code is correct; in the local scope, AnnounceTime is first set, executed, and then removed from the delegate object.
{
Announce an = new announce ();
Action Act = New Testharness.An (An.announCetime);
Testharness.Tester = ACT;
Testharness.run ();
Testharness.tester - = ACT;
}
Our initial idea for designing the TestHashTable Class is that a destructuring function is implemented to remove the test method added in the constructor. However, the destructor call mechanism in C # is not large in C [5]. The destructor of the C # will neither be evoked by the end of the object life, and will not be evokes directly because of the release of the last reference handle (Reference Handle). In fact, the destructive function is only called when the garbage collector is garbage collection, and the timing of the garbage collection is generally unpredictable, and even the garbage collection is not implemented.
C # specifies that the resource-derived action is placed in a method called Dispose, and the user can call the method directly:
Public void dispose ()
{
Testharness.tester - = new testharness.action (TEST0);
Testharness.tester - = new testharness.action (TEST1);
}
If a certain category defines a despage function, it usually evokes Dispose.
Access the underlying category interface
Let's look back at the previous code:
{
Announce an = new announce ();
Action ACT =
New Testharness.Action
(An.announcetime);
Testharness.Tester = ACT;
Testharness.run ();
Testharness.tester - = ACT;
}
Another implementation is that Tester is now equipped with other methods. If so, save the current delegate list, reset the Tester to ACT, then call the run, and finally restore the Tester to the original status. We can use the underlying Delegate class interface to know the number of methods actually equipped with Delegate. E.g,
IF (Testharness.Tester! = NULL &&
Testharnest.getinvocationList (). Length! = 0)
{
Action Oldact = Testharness.tester;
Testharness.Tester = Act;
Testharness.run ();
Testharness.tester = Oldact;
}
Else {...}
GetInvocationList returns a method of Delegate Class Objects array, and each element of the array represents a method currently equipped with the Delegate. Length is a property (attribute) of the underlying Array Class. Array Class implements a C # built-in array of semantics [6].
With the Delegate Class's Method Property, we can get all the runtime information of the mutation. If the method is non-static, then we can more acquiring the objects of the method (the translation: that is, the object to which the method belongs to the class of the method) is more efficient. In the following example, DELEGATE's Methods (Method) and Properties are represented by red:
IF (Testharness.Tester! = Null)
{
Delegate [] methods = test.tester.getinvocationList ();
Foreach (Delegate D in Methods)
{
MethodInfo thefunction = D.Method;
TYPE THTARGET = D. Target.gettype ();
/ Ok: Now we can know all the information of the method of Delegate.
}
}
to sum up
I hope this article can cause you to interested in C # delegate Type. I think Delegate Type provides an innovative "Poin Class Method" mechanism for C #. Perhaps this article also caused you to be interested in C # languages and .NET Class Framework.
A Good Starting Page for Technical Resources Is
I would like to thank Josee Lajoie and Marc Briand for their thoughtful review of an earlier draft of this article. Their feedback has made this a significantly better article. I would also like to thank Caro Segal, Shimon Cohen, and Gabi Bayer of you- NiVersity.com for providing a safty.net.
Comment
[1] For C programmers, there are two points worthy of questions: (a) need to put a pair of curabinal arcs after the object's type name as Default Constructor, and (b) in square brackets to be marked in array Between the type and array name.
[2] The array built in C # is an object of the Array Class provided by .NET Class Library. Array Class static methods and non-static methods can be used by C # built-in objects. COPYTO is a non-static method of Array.
[3] Like Java, the member statement in the C # includes its access level. The default access level is Private.
[4] Similar, C standard requirements, the referenced temporary object must be destroyed until the end of the referenced life.
[5] In internal implementation, the destructor does not even exist. A class of collations will be converted to the Virtual Finalize method.
[6] In C #, a conditional discriminant result must be boolean type. Direct discrimination of Length values, such as IF (Testharness.length), is not a legal condition judgment. The integer value cannot be implicitly converted to the Boolean value.
Stanley B. Lippman is IT Program Chair with you-niversity.com, an interactive e-learning provider of technical courses on Patterns, C , C #, Java, XML, ASP, and the .NET platform. Previously, Stan worked for over five years in Feature Animation both at the Disney and DreamWorks Animation Studios. He was the software Technical Director on the Firebird segment of Fantasia 2000. Prior to that, Stan worked for over a decade at Bell Laboratories. Stan is the author of C Primer, Essential C , and INSIDE THE C Object Model. He Is Currently at Work On C # primer for the developmentor book series for addison-wesley. He May be reached at stanleyl@you-niversity.com. Translation
[Translation 1] In C #, the so-called "Method" ", in fact, it means that the member function we usually understand, its literal meaning is very close to" Function ".
[Translation 2] The author has this statement on the Delegate Type action of the aforementioned Delegate Type Action. In general, as long as the return type of Methods is the same and the parameters are the same, it can be equipped with the same delegate Type.