.NET Windows Forms in a Nutshell [Book]
Any type will need to define some members to be of any use. Members are either associated with data or behavior. In C++ this means fields and methods, respectively. In addition to these, which the CLR supports, the CLR adds some new member types. All these member types are described here.
The public keyword in both languages indicates that any code is allowed to call this method. We will talk more about such protection keywords towards the end of this section.
The method takes a string as a single parameter and returns an integer. The code for the method attempts to convert the string to an integer by using the C# int type’s or VB Integer type’s static Parse method. (Both int and Integer are identical to the .NET Framework’s System.Int32 type.) MyMethod is an instance method—users of MyFirstClass will need an instance of MyFirstClass to call this method.
Methods can either be instance methods or static methods. (Instance methods are the default, but you can use the static keyword in C# or the Shared keyword in Visual Basic to specify a static method.) Instance methods are invoked with respect to a particular object or value, and they have access to that object through the this keyword in C# and the Me operator in VB. They can also refer to members simply by their names—if they are instance members, the this or Me reference will be used implicitly. Static methods do not need an object in order to be invoked, but they will only have access to other static members of the class. Visual Basic is not trying to maintain any look-and-feel compatibility with C++, so it uses the rather more sensible Shared keyword for members that are shared across all instances of a class.
Methods are where we define code. In most .NET languages, all code must be defined in a method of some type. (Because properties also can contain code, they would appear to be an exception, but they are actually implemented by .NET language compilers as method calls.) As with C++, the method must have a signature (consisting of its name and the types of parameters it takes), and that signature must be different from any other methods defined in the same class. Overloading is allowed, i.e., the names of two methods can be the same if their signatures are different. Methods must also have a return type (even if the method returns void or Nothing ); overloading based on return type alone is not allowed. (C++ doesn’t allow this either.) Note that methods that return void or Nothing in VB are declared using the Sub statement rather than the Function statement.
This class defines a private instance field called x , which can store an integer. The method IncrementTotal adjusts this field and returns its value. The code does not use the this or Me reference; it just refers to x by name. The compiler will detect that the code refers to the instance field x and presume that the author meant this.x or Me.x .
Fields hold data. As with methods, fields can be either instance or static. If a field is declared as static, it is singular—all instances of the class or value will share the same piece of data, and that data will be accessible to instance and static methods alike. But instance fields (the default) are stored as part of each instance of the type, so every instance has its own set.
In this particular example, there is no field. (Feel free to implement your own properties using private fields internally.) This will just run the get and set methods defined for the property. In this case, reading the property will always get the value 42, and writing it will just cause a message to be printed. Most properties behave more usefully, of course, but the point is the client code is not dependent on how the property works—it could rely on a normal field, derive the value from those in other fields, or retrieve the value from a database. The client just accesses the property, and the object can handle that however it sees fit.
The use of {0} in the string passed to Console.WriteLine indicates that the parameter following the string should be inserted into the output at this point. It has a similar role to placeholders such as %d in the format string for printf in C.
This defines an int or Integer property called MyProp . Note that value is a keyword in C# and VB, and it is used in property set functions. It is the value that the caller is trying to give the property. (In this case, we are just writing that value to the console.)
Just as COM did, .NET specifies a standard way of exposing properties through methods. And as with COM, some languages (including Visual Basic .NET and C#) provide special syntax to support this, allowing field-like syntax to be used when reading or writing properties, even though they are implemented in terms of methods. So in C#, we can provide properties like this:
It is considered good practice never to expose a data field as a public member of an object, because that would cause client code to become too tightly coupled with that type’s implementation. Exposing properties through get and set methods is a popular technique for allowing components’ implementations the flexibility to evolve while still providing public members that feel like fields.
Event handling
Components often need to notify client code when something interesting has happened. This is particularly common in user interface code—applications need to know when buttons are clicked, when windows are resized, when text is typed in, and so on. .NET defines a standard way in which objects can deliver event notifications to their clients. Visual Basic and C# both have special syntax for declaring and consuming such events. These two syntaxes are quite different—C# presents the CLR’s event handling mechanisms directly, while VB uses a style that is much more like the event handling in previous versions of VB. However, both languages are based on the same fundamental mechanisms, so they have much in common.
A class that wishes to be able to raise events (most Windows Forms controls do this) must declare the fact by adding a special member for each type of event it can raise. In C#, we use the following syntax:
public class EventSource {public event MouseEventHandler MouseDown;
. . . }
In Visual Basic, the equivalent event declaration looks like this:
Public Class EventSourcePublic Event MouseDown As MouseEventHandler
. . . End Class
Both examples declare an event whose name is
MouseDown
and whose type is
MouseEventHandler
. (The
MouseEventHandler
type is defined in the
System.Windows.Forms
namespace, and we will see
its definition later.) As it happens, all Windows Forms controls
support this event—it is raised whenever a mouse button is
pressed while the cursor is over the control.
When an event occurs, the event source notifies the client by calling
the relevant handler function. The way we determine which particular
function it calls is different in VB and C#. In VB, the class that
wishes to receive the event simply uses the
WithEvents
keyword to indicate that it is
interested in events from the event source object. It then identifies
a particular method as being the handler for a given event using the
Handles
keyword. The signature of the handler
method must match the type of the event. In this case, the event is
of type MouseEventHandler
. (We will look at this
type’s definition shortly.) So our code looks like
this:
Public Class EventReceiverPrivate WithEvents src As EventSource
. . . Private Sub src_OnMouseDown( _ sender As Object, e As MouseEventArgs) _Handles src.MouseDown
Console.WriteLine("src object raised MouseDown event") End Class
This style is similar to how previous versions of Visual Basic handled events. However, it hides the details of how events really work. C# does not provide such a level of abstraction—it exposes the CLR’s underlying mechanisms directly. Consequently, we need to do slightly more work in C# to handle events. Moreover, we must understand the mechanism on which events are based.
The CLR provides a special kind of object that is used to connect an
event source to its corresponding event handler method. These special
objects are called
delegates. Delegates
are .NET’s nearest equivalent to function
pointers—they hold typed references to functions. As with a C++
function pointer, a delegate’s type
(MouseEventHandler
, in this case) determines the
signature that the client’s handler function must
have. MouseEventHandler
is defined (in
System.Windows.Forms
) thus:
public delegate void MouseEventHandler( object sender, MouseEventArgs e);
The equivalent Visual Basic definition is:
Public Delegate Sub MouseEventHandler( _ sender As Object, e As MouseEventArgs
So if we wish to receive MouseDown
event
notifications from some control, we must provide a function with a
matching signature:
private void OnMouseDown (object sender, MouseEventArgs e) { ... handle the MouseDown event ... }
Of course, we must also tell the control that we are interested in
the MouseDown
event and would like notifications
to be delivered to our OnMouseDown
method. In
Visual Basic, we did this by using the Handles
keyword, but in C#, we must create a
MouseEventHandler
delegate initialized with a
reference to our method, and then attach it to the relevant event on
the control, using the following rather strange syntax:
src.MouseDown += new MouseEventHandler(OnMouseDown);
This is roughly equivalent to passing the address of a callback
function as a function pointer in C++; the delegate acts as a typed
reference to a function that can be passed as a parameter or stored
in a field so that the function can be called back later. But we
can’t use function pointers as we would in C++, and
not just for the ideological reason that it doesn’t
enter into the spirit of the brave new pointerless world of the CLR.
There is a rather more prosaic reason not to use raw function
pointers: JIT compilation means that functions don’t
necessarily remain in the same place for the life of a program. In
fact, when the code above is run, there is every chance that the
OnMouseDown
method has not been JIT compiled at
all yet, so it might not even have an address. So instead, we rely on
delegates to provide us with behavior equivalent to function
pointers, while shielding us from the complexities of using pointers
in the world of movable code.
Delegates can hold an object reference as well as a function
reference. (In C++ terms, this would mean that a delegate is really
two pointers—a function pointer and a pointer to an object.) In
the example above, OnMouseDown
is not a static
function, so it can only be invoked in conjunction with an object
reference. (The value for the implicit this
reference has to come from somewhere.) If a function requires an
object reference, a suitable one must be supplied when a delegate to
that function is created. This can be done explicitly, for example:
myDelegate = new MyDelegateType(myObj.MyMethod);
creates a new delegate whose type is
MyDelegateType
and attaches it to the
MyMethod
method on the object to which
myObj
refers. (Delegates store their own copy of
the reference, so if the myObj
variable is later
modified to refer to a different object, the delegate will still
refer to the original one.) Or the object reference can be
inferred—if the delegate is created in the scope of a
non-static method, the this
reference will be used
if no explicit reference is supplied. The
MouseEventHandler
example above illustrates this,
and is typical of code inside a form’s
initialization function: because an object reference has not been
supplied explicitly, the C# compiler automatically supplies a
reference to whichever form is being initialized. That code is
shorthand for the following:
src.MouseDown += new MouseEventHandler(this.OnMouseDown);
This use of the +=
syntax, peculiar to C#, is
simply shorthand for a method call. For each event that a class
defines, the C# compiler will actually define two methods, one for
adding a handler and one for removing it. C# hides this detail with
the +=
syntax (and the corresponding
-=
syntax used for disconnecting an event
handler), and it also shields us from the details of declaring events
if we wish to raise them ourselves. If we add an event declaration
such as the one shown above to our own class, the C# compiler will
automatically generate the functions to add and remove event handlers
for us; the code it generates is able to cope with multiple event
handlers being attached simultaneously, as all events should.
Note that the -=
syntax used for detaching an
event handler is smart enough to work out which method a delegate
refers to. It doesn’t require the same delegate
object that was used in the +=
to be passed back
in. So looking at the code above, you might have thought that we
would need to store the delegate being created with the
new
operator to pass it back when we wish to
detach. In fact, it works just fine if we create a new delegate at
detachment time:
src.MouseDown -= new MouseEventHandler(this.OnMouseDown);
In Visual Basic, all these details of creating delegates and
attaching them are hidden—using the
WithEvents
and Handles
keywords
causes all this code to be generated automatically. However, VB also
supports the explicit style that C# requires. The syntax is
different, but the meaning is the same. We can create a delegate
object using VB’s AddressOf
keyword. And VB’s equivalents to the
+=
and -=
event operators are
the AddHandler
and
RemoveHandler
keywords. So we can add a handler
explicitly, just as we are required to in C#, with the following VB
code:
AddHandler src.MouseDown, AddressOf Me.OnMouseDown
And the corresponding code to remove a handler is:
RemoveHandler src.MouseDown, AddressOf Me.OnMouseDown
Most of the time, you would not need to use this explicit style in Visual Basic. However, it can be useful for attaching handlers dynamically at runtime. In addition, if you want a single event handler to handle an event from every object in a collection, you will need to use this explicit style.
All the event handler delegates defined in the .NET Framework follow
a common pattern. They define function signatures that take two
parameters. The first parameter is always of type
object
, and is a reference to the object that
raised the event. (So when a control raises the
MouseDown
event, it passes a reference to itself
to the event handler. This can be useful it you want to have events
from multiple controls on a form all handled by a single
function—this parameter lets it know which control a particular
event came from.) The second parameter contains information about the
event. The various delegates defined in .NET all specify different
types for this second parameter. For example, the drag-and-drop
events use a delegate type called
DragEventHandler
, which defines the second
parameter to be a DragEventArgs
, while
MouseEventHandler
(see above) defines it to be a
MouseEventArgs
. Some events provide no special
information—for example, the Click
event
raised by a button simply indicates that a particular button has been
clicked, so there is no use for a final parameter. .NET defines a
generic delegate for such methods:
public delegate void EventHandler(object sender, EventArgs e);
The second parameter is usually a special value,
EventArgs.Empty
. This may seem pointless—if
the same value is passed every time, why not just leave off the
second parameter? It is left there just in case peculiar
circumstances arise in which it would be useful to be able to pass
some information. For example, if you were to define a custom
derivative of the standard Button
class, you might
wish to pass some information in your Click
event.
If you define a class that derives from EventArgs
,
you can pass it as the second parameter. If
EventHandler
didn’t provide this
second argument, you would not be able to do this.
Note that you are not required to use this style of event handling
for your own components. You can define classes whose events use a
delegate of your own devising, which may have any signature you like.
Of course, if you stick to the framework’s style,
your code will look more consistent, so it is recommended that you do
this. But there is nothing magic about delegates whose first
parameter is an object
and whose second parameter
is some type deriving from EventArgs
.
What is the difference between nettype and “wire logic”. Can you please explain with example?
I Know the below details.
For “wire logic” there is an inbuilt resolution function and for “nettype”we can have our own resolution function, we can drive whatever the data that we want to drive (4-state data, 2-state data,real value data),i.e., we can define the type of driving data in case of “nettype”.
Can you please explain with some examples on advantages of nettype over “wire logic”?
.NET Windows Forms in a Nutshell [Book]
Difference between nettype and "wire logic"
- Previous: Introduction to .NET Framework
- Next: Net Data Type