Adding Field and Property Members

Team-Fly    

 
Visual Basic .NET Unleashed
By Paul Kimmel
Table of Contents
Chapter 7.  Creating Classes

Adding Field and Property Members

A field is a data member of a class. Fields can be ValueType members, like Integers or Dates, or can be aggregate types, like structures, enumerations, or classes. A property is a special member construct that is used like a field, but acts like a method. Properties are special kinds of methods that generally are used to provide constrained access to fields.

As a general rule, fields are private elements of a class. If access is provided to a field, the access is provided using a property. For this reason, fields are generally private and properties are generally public. However, sometimes fields aren't exposed through properties or at all. Properties don't always represent a field, either. Sometimes properties represent data persisted in a database, a registry or INI file, or some other underlying value, not a field.

The motivation for making fields private is that unrestrained access to data is inherently risk-inducing. Consider the steps for making Hollandaise sauce. Cook the yolks too quickly and you have scrambled eggs. However, by slowly introducing heatthe dial on the stove being analogous to a property for raising or lowering heatyou get a nice sauce consistency.

The dial on the stove is a metaphor for the property method. The field would be heat, and the dial represents the property for modifying the heat. The operator of the stove isn't allowed to increase fuel beyond a specific point. Listings 7.1 and 7.2 demonstrate two partial classes representing the stove and the throttle setting for a fuel system.

Listing 7.1 A class representing the field and property for a stove temperature setting
  1:  Public Class Stove  2:   3:  Private FTemperature As Double  4:   5:  Public Property Temperature() As Double  6:   7:  Get  8:  Return FTemperature  9:  End Get  10:   11:  Set(ByVal Value As Double)  12:  Debug.Assert(Value < 400 And Value >= 0)  13:  If (FTemperature >= 400) Then Exit Property  14:  FTemperature = Value  15:  Debug.WriteLine(FTemperature)  16:  End Set  17:   18:  End Property  19:   20:  End Class 

Listing 7.1 introduces the field FTemperature as a Double. The FTemperature field is exposed through the Temperature property. The property Get method returns the underlying field value, and the property Set method assigns the new value to the underlying field value. Line 13 uses an assertion to ensure that no inappropriate temperature settings get past you, the developer, while you are building the system. When you deploy the class, the Debug.Assert method will be disabled by the compiler. If Stove were being used to control an actual stove, you would absolutely not want a temperature setting beyond the safe operating limit of the physical stove. In addition, we never ship a class without sanity checking for deployment. To make sure the temperature setting is safe when we ship the stove, we use a mirrored If-condition check to keep the temperature setting within safe operating limits.

Let's return to our discussion of grammar. You will note that I used an F-prefix for the field and dropped the F to contrive the property name . Notice also that the declaration for properties has changed. In Visual Basic .NET, the property statement is written as a single block structure. The Get method and Set method are nested blocks between the Property and End Property statements, defining the property block. (Recall that in VB6 the property getter and setter methods were defined as two separate and distinct blocks.)

Listing 7.2 demonstrates a new class named FuelSystem, which uses a different technique for ensuring that the throttle settings are within an acceptable range.

Listing 7.2 Using an enumeration to constrain the field setting, combined with the property methods
  1:  Public Class FuelSystem  2:   3:  Public Enum ThrottleSetting  4:  Idle  5:  Cruise  6:  Accelerate  7:  End Enum  8:   9:  Private FThrottle As ThrottleSetting  10:   11:  Public Property Throttle() As ThrottleSetting  12:   13:  Get  14:  Return FThrottle  15:  End Get  16:   17:  Set(ByVal Value As ThrottleSetting)  18:  FThrottle = Value  19:  Debug.WriteLine(Value)  20:  End Set  21:   22:  End Property  23:   24:  End Class 

The enumeration ThrottleSetting defines three valid settings for our throttle: Idle, Cruise, and Accelerate. If you combine the Option Strict setting with the enumeration, clients won't be able to set an inappropriate Throttle setting. Option Strict will ensure that all values passed to the Property setter is a ThrottleSetting value, and is consequently one of Idle, Cruise, or Accelerate.

Encapsulation and Properties

Access specifiers can be applied to any member of a class. Most often properties will be declared as Public members of a class or structure. You aren't precluded from defining properties with any other access specifier , however.

One strategy where Protected properties are employed is in classes that you intend for consumers to generalize. By declaring a class with Protected properties, you are allowing generalizers to decide whether or not to promote those properties to Public access.

The access specifier is placed before the Property keyword. If you don't explicitly add an access specifier, members will have Public access. Refer to Listings 7.2 and 7.3 for examples of the placement of the access specifier.

Tip

Explicitly type all access specifiers. The result will be code that reads as precise and intentional.


Defining Indexed Properties

Indexed properties are simply property methods that have a mandatory parameter. The mandatory parameter is semantically treated like an index, but after your code is in the property method, you can do anything you want to with the parameter. Let's quickly review properties and contrast basic properties with indexed properties.

A basic vanilla property statement has a getter and setter method. The getter method behaves like a function and is implicitly called when the property is used as a right-hand value. The setter acts like data used as a left-hand value, and sets the underlying value of the property. In its simplest incarnation, a property's value is stored in an underlying field. Both the property and the field have the same data type. Listing 7.2 demonstrates a basic property, including a getter, setter, and field value. Notice the property statement on line 11 of Listing 7.2. The property statement indicates a return type but takes no parameters.

In contrast, an indexed property has an argument between the parentheses. This argument doesn't represent the field value; rather, it represents an index to an underlying field value. The implication of the presence of an index is that the underlying field must be an array or collection. This is usually the case, too. However, the index can be used for anything, and the index parameter doesn't have to be a numeric value.

The fundamental idea behind indexed properties is that you can wrap arrays and other kinds of collections of data safely behind property methods. The motivation for doing so is the same as the motivation for wrapping singular data in property methods: You want to protect the data from abuse. Listing 7.3 demonstrates two indexed properties. Both actually refer to the same underlying array, but interact with the array in two different ways.

Listing 7.3 Indexed properties
  1:  Public Class Indexed  2:   3:  Private FStrings() As String = {"One", "Two", "Three"}  4:   5:  Public Property Strings(ByVal Index As Integer) As String  6:  Get  7:  Return FStrings(Index)  8:  End Get  9:  Set(ByVal Value As String)  10:  FStrings(Index) = Value  11:  End Set  12:  End Property  13:   14:  Private Sub Swap(ByVal OldIndex As Integer, _  15:  ByVal NewIndex As Integer)  16:   17:  Dim Temp As String = FStrings(NewIndex)  18:  FStrings(NewIndex) = FStrings(OldIndex)  19:  FStrings(OldIndex) = Temp  20:  End Sub  21:   22:  Public Property Names(ByVal Name As String) As Integer  23:  Get  24:  Return Array.IndexOf(FStrings, Name)  25:  End Get  26:   27:  Set(ByVal Value As Integer)  28:  Swap(Names(Name), Value)  29:  End Set  30:  End Property  31:   32:  End Class 

There are two indexed properties in the class Indexed. The first begins on line 5, is named Strings, and takes an Integer argument. The parameter is named Index and literally acts like an index into the array of strings field, FStrings. Notice how the index is used to index the array in both the getter on lines 6 to 8 and the setter on lines 9 to 11. Lines 7 and 11 demonstrate an intuitive use of an index and an array. The getter is called when an instance of Indexed and the Strings property is used as an r-value, as in MsgBox(Indexed.Strings(1)). The setter is used when the Indexed.Strings property is used as a l-value, as in Indexed.Strings(0) = "One".

Compare the Strings property with the Names property. As with the Strings property, the argument to the Names property plays the role of index. The type in the return type positionAs String on line 5 and As Integer on line 22represents the data type of the property. Hence Strings is an indexed String property and Names is an indexed Integer property. Names takes a string argument, Name, and returns the indexed position of that string in the underlying array.

Line 24 uses the Shared method System.Array.IndexOf, passing the array and the object to search for, and returns the index of the item in the array. Line 28 calls Swap using the current index of the existing element (found using the getter) and the new index presented by Value. The array element is swapped from the old position to the new position. The following statements demonstrate how you might find the Names property used in code:

 Dim MyIndexed As New Indexed MyIndexed.Names("One") = 1 

After the code on the second line runs, the underlying fieldthe array FStringsis equivalent to {"Two", "One", "Three"}. The value at the position represented by the name index of "One" is moved to index 1 by swapping with the value at index 1.

Benefits of Indexed Properties

The benefits of using indexed properties are multifold. An obvious benefit is that data should be protected by methods and easy to use like data. Just because the data happens to be an array or collection doesn't mean that it should be unprotected against abuse. Perhaps a not-so-obvious benefit is that more complex collections of data can be made easier to use by indicating that an indexed property is a default property. (More on indexed properties in a moment.)

Listing 7.3 doesn't demonstrate protecting the data from abuse. In an application we would need to provide some sanity checking to make sure that bad indexes or names weren't being used in our sample program. Let's look at some revisions we might make to make the code more robust. We'll begin with the Indexed.Names getter.

What would happen if I were to request Indexed.Names("Four")? Based on the existing implementation, I would get a -1 from the Array.IndexOf method. This is suitable, so we will leave the Names Get method as is. Alternatively, we could raise an exception if a name wasn't found, but anyone trying to use an invalid index will get an exception already. As a result, our choice is good enough.

Next, let's look at the Names property Set method. There is no sanity checking here. I can test the code to verify what the default behavior is. If a bad name-index is used, I should get an exception. In fact, Indexed.Names("Six") = 5 raises a System.IndexOutofRangeException. The caller probably needs to know that the statement failed; consequently, the exception is reasonable. On the other hand, we might decide that the code provides overly general information when the default exception occurs (see Figure 7.2).

Figure 7.2. The default exception raised when an invalid index is used with a System.Array object.

graphics/07fig02.jpg

Tip

For practical purposes, use the default exception if it provides reasonable feedback. You can always layer in extended behavior if you really need to.


For demonstration purposes, we will suppose that the default exception behavior isn't sufficient. We will implement extended behavior to provide more precise feedback to consumers of the Indexed class.

Revising the Names Property Set Method

The practical revision assumes that we want to revise our indexed Set property method to specifically tell the consumer that the index is invalid. Listing 7.4 shows just the revisions to the Indexed class from Listing 7.3.

Listing 7.4 Adding custom revisions to invalid index-handling behavior
  1:  Private Function ValidIndex(ByVal Index As Integer) As Boolean  2:  Return (Index >= FStrings.GetLowerBound(0)) And _  3:  (Index <= FStrings.GetUpperBound(0))  4:  End Function  5:   6:  Private Overloads Sub Validate(ByVal Name As String)  7:  Validate(Names(Name))  8:  End Sub  9:   10:  Private Overloads Sub Validate(ByVal Index As Integer)  11:  If (Not ValidIndex(Index)) Then  12:  Throw New ApplicationException(Index & " is an invalid index")  13:  End If  14:  End Sub  15:   16:  Public Property Names(ByVal Name As String) As Integer  17:  Get  18:  Return Array.IndexOf(FStrings, Name)  19:  End Get  20:   21:  Set(ByVal Value As Integer)  22:  Validate(Name)  23:  Swap(Names(Name), Value)  24:  End Set  25:  End Property 

The revision to the Names setter property method includes a call to a Validate function. We already know that we could write validation inline, but in earlier chapters, we discussed the motivation for the refactoring "Extract Method." Essentially, we factored out the Validate behavior to keep the setter property method simple and to create reusable behavior in the form of a Validate procedure. The code introduces two forms of Validate using the Overloads modifier. One form of Validate takes a name and is implemented in terms of the other version, which takes an index. (Thinking ahead, we might need to validate the Strings property that uses an integer index.) To make the code more readable, we implement the actual index validation in a well-named method, ValidIndex (lines 1 through 4). Line 12 throws an exception if the index is invalid.

Notice that there are no comments. We substituted small, singular, well-named methods for comments that would be superfluous. If the Indexed class represented a significant abstraction, we might make an additional revision.

Subclassing an Exception Class

Suppose for the sake of argument that we want to use the exception-throwing behavior on line 12 of Listing 7.4 in several places and perhaps several classes. Also, suppose that the Indexed class represents a significant or important abstraction in our system.

Note

You can find more details on exception handling in Chapter 5 and more on inheritance in Chapter 10.


We could introduce a new exception class that neatly encapsulates managing the invalid index. This is accomplished by subclassing an existing exception class and extending the behavior. This is perfectly acceptable to do, and developers have been extending exception classes in other languages for years .

We have established for our purposes that the Indexed class is important and that we need to reuse the abstraction, representing bad-index exceptions. The Visual Studio .NET help suggests that if we want to create our own application exceptions, we should subclass the System.ApplicationException class, so that's what we will do.

Our error condition occurs when a bad index is passed to the Indexed class's Names property Set method. The exception is raised on line 12 of Listing 7.4 when validation fails. This behavior is functional, so we won't change the orchestration of the code. We will, however, change the objects involved. The revision is demonstrated in Listing 7.5.

Listing 7.5 The complete listing of the Indexed class, including a new exception class
  1:  Public Class IndexedException  2:  Inherits ApplicationException  3:   4:  Public Sub New(ByVal Str As String)  5:  MyBase.New(Str)  6:  End Sub  7:   8:  Public Shared Sub ThrowException(ByVal Index As Integer)  9:  Throw New IndexedException(Index & " is an invalid index")  10:  End Sub  11:   12:  End Class  13:   14:  Public Class Indexed  15:   16:  Private FStrings() As String = {"One", "Two", "Three"}  17:   18:  Public Property Strings(ByVal Index As Integer) As String  19:  Get  20:  Return FStrings(Index)  21:  End Get  22:   23:  Set(ByVal Value As String)  24:  FStrings(Index) = Value  25:  End Set  26:  End Property  27:   28:  Private Sub Swap(ByVal OldIndex As Integer, _  29:  ByVal NewIndex As Integer)  30:   31:  Dim Temp As String = FStrings(NewIndex)  32:  FStrings(NewIndex) = FStrings(OldIndex)  33:  FStrings(OldIndex) = Temp  34:  End Sub  35:   36:  Private Function ValidIndex(ByVal Index As Integer) As Boolean  37:  Return (Index >= FStrings.GetLowerBound(0)) And _  38:  (Index <= FStrings.GetUpperBound(0))  39:  End Function  40:   41:  Private Overloads Sub Validate(ByVal Name As String)  42:  Validate(Names(Name))  43:  End Sub  44:   45:  Private Overloads Sub Validate(ByVal Index As Integer)  46:  If (Not ValidIndex(Index)) Then  47:  'Throw New ApplicationException(Index & " is an invalid index")  48:  IndexedException.ThrowException(Index)  49:  End If  50:  End Sub  51:   52:  Public Property Names(ByVal Name As String) As Integer  53:  Get  54:  Return Array.IndexOf(FStrings, Name)  55:  End Get  56:   57:  Set(ByVal Value As Integer)  58:  Validate(Name)  59:  Swap(Names(Name), Value)  60:  End Set  61:  End Property  62:   63:  End Class 

The new exception class is defined on lines 1 through 12. Line 2 indicates that IndexedException inherits from System.ApplicationException. In object-oriented parlance, this is referred to as an IsA relationship. The Unified Modeling Language (UML) refers to inheritance as a generalization. We say that IndexedException is an ApplicationException.

IndexedException introduces an overloaded constructor that takes a string argument. The string argument to the constructorline 4will become part of the message of our exception. Line 5 uses the MyBase variable, which references the base class. MyBase is available just as the reference-to-self variable Me is. Line 8 introduces a Shared method. A Shared method that creates an instance of an object is referred to as a factory method. A factory method allows you to localize the construction and initialization of an object, mitigating the need to duplicate object construction code. Finally, the Validate procedure replaces the old statement from line 12 of Listing 7.4 with a call to the new factory method that creates and throws our new exception. (The original statement and the new statement are on lines 47 and 48, respectively, of Listing 7.5.)

Now you know the mechanics of defining indexed properties, adding validation code to the property methods, and how to introduce new exception-handling classes. If inheritance is a little confusing, you will find an elaboration on the subjects of inheritance and polymorphism in Chapter 10.

Using Default Properties

Visual Basic .NET supports Default properties, but unlike properties in VB6, only indexed properties can be Default properties in Visual Basic .NET. The reason is straightforward.

In VB6, we used the Set method when assigning an object reference to an object variable. Again, in VB6, if the assignee in a statement were an object and no Set were present, VB6 could infer that the coder intended to use the Default property. Hence, VB6 Default properties don't have to be indexed properties. The Set keyword provided the necessary compiler clue.

Visual Basic .NET doesn't use Set for object assignment. Thus the presence of an object and the absence of Set aren't sufficient to convey your intent to the compiler. In Visual Basic .NET, it's the presence of parentheses used with an object that provides the compiler hint indicating that a Default property is being requested . Consequently, all Default properties must be indexed properties in Visual Basic .NET, and you might have only one default property.

Note

Only indexed properties can be the Default property in Object Pascal too. The presence of object [] in Object Pascal provides a similar compiler hint that an indexed property rather than object assignment is being requested.

It's possibleand very probablethat the revision to default properties is due in part to the influence of Anders Hejlsberg. Hejlsberg, now a Distinguished Engineer at Microsoft, was a principal architect at Borland, instrumental in the implementation of Delphi, which is built in Object Pascal. There are many such influences in Visual Basic .NET from Object Pascal. This isn't to say that Visual Basic .NET is Pascal-like; rather, Visual Basic .NET is an evolving language and Microsoft has the good sense to improve Visual Basic with a heterogeneous mixture of aspects from other languages.

Granted, it might take some getting used toI heard some developers referring to Visual Basic .NET as "Visual Fred" at Tech EdVisual Basic .NET is a radically improved product, but it's still Visual Basic.


To indicate that a property is the default property, place the Default keyword on the line of text that starts the Property statement, preceding the access specifier. An excerpt from Listing 7.5 shows the placement of the Default keyword, making the Strings property the default property:

 Default Public Property Strings(ByVal Index As Integer) As String   Get     Return FStrings(Index)   End Get   Set(ByVal Value As String)     FStrings(Index) = Value   End Set End Property 

Assuming we declare an instance of Indexed using Dim IndexedObject As New Indexed, we can access the Strings property in the original verbose manner:

 MsgBox(IndexedObject.Strings(1)) 

Or we can access it in the shorthand manner, relying on the Default modifier:

 MsgBox(IndexedObject(1)) 

To recap, default properties must be indexed properties, you can only have one default property per class, and you can invoke property setter and getter methods on a default property using the verbose or shorthand form. The compiler will reconcile the code as needed.

Using Property Modifiers

In addition to the access specifiers, there are special property modifiers that are particular to properties only. These include the ReadOnly and WriteOnly modifiers.

A read-only property is a property that can be used as an r-value only. That is, a property statement that includes a ReadOnly modifier will generate a getter block only and users can evaluate the property but not modify it. Consumers of a class can think of ReadOnly properties as constants or immutable data, but keep in mind that a mechanism internal to the object can change the underlying value even though the consumer can't.

A write-only property is a property that a consumer can modify but can't view. Write-only properties implement a property setter only.

The outer property block is identical for ReadOnly or WriteOnly properties, except for the presence of the modifier. Internally, ReadOnly properties have a getter and no setter, and WriteOnly properties have a setter and no getter.

Implementing Read-Only Properties

An example of a read-only property might be one that returns a datetime stamp when an object is created. Because an object can only be created oncealthough you can have multiple instances of a class, any given instance will be created only onceit doesn't make sense to allow consumers to modify the creation time of an object.

If you need to track how long an object has been alive and want to make sure that consumers cannot change the creation time stamp, you can define a read-only CreateTime property and initialize it in the constructor:

 Public Sub New()   MyBase.New()   FCreateTime = Now End Sub Private FCreateTime As Date Public ReadOnly Property CreateTime() As Date   Get     Return FCreateTime   End Get  End Property 

Note the location of the ReadOnly modifier. The Public property CreateTime is defined as a ReadOnly property, hence it only has a Get block. The constructorSub Newinitializes the underlying field, FCreateTime, when the constructor is called.

Another example where you might elect to employ a read-only property is a calculated field. If a property has no underlying field value and its value is derived, it makes no sense to implement a set property method. Consider the symmetric ElapsedTime property. If we wanted to determine the elapsed application runtime, we could return the elapsed time as the current time minus the CreateTime:

 Public ReadOnly Property ElapsedTime() As TimeSpan   Get     Return Date.op_Subtraction(Now, FCreateTime)   End Get End Property 

The ElapsedTime property is modified ReadOnly because it's dynamically derived each time it's called. The ElapsedTime property also demonstrates the new TimeSpan class, which was implemented to allow for very close time measurements. Another peculiarity is the Shared method Date.op_Subtraction invocation. Visual Basic .NET doesn't yet support operator overloading, so specially named methods with an op_ prefix were implemented where the behavior represents an overloaded operator that might exist in C# for example.

ElapsedTime returns the difference between Now and the containing object's creation time. You can test the two properties with a couple of lines of code:

 Dim O As New PropertyModifiers() System.Threading.Thread.Sleep(1000) MsgBox(O.ElapsedTime.ToString) 

The first statement creates an instance of the PropertyModifers class containing the two predefined properties we have been discussing. The constructor initializes the field FCreateTime. The second statement uses the Shared Sleep method, which puts the current Thread to sleep. In the example, the thread is put to sleep for 1000 milliseconds , or 1 second. The third statement invokes the ElapsedTime property method, which returns a TimeSpan object. The ToString method is actually being called using the implicit reference to the TimeSpan object.

The three sample statements culminate in displaying a message box as shown in Figure 7.3. According to the message box, it looks as if Visual Basic .NET required about 1.4 milliseconds after the thread woke up and the Now function was called.

Figure 7.3. Shows the granularity of the TimeSpan class in Visual Basic .NET.

graphics/07fig03.jpg

Implementing WriteOnly Properties

WriteOnly properties are implemented less frequently, but you will encounter some good reasons for using them on occasion. One occasion is a password property.

Suppose you have a user-validation class that accepts a user name and masked password. It might be perfectly acceptable to answer a client object's query, "Who is the user?" But it would probably be a riskier proposition to answer "What is the password?" In such an instance, you might want to allow the user to enter the password but prevent any client objects from obtaining this information. You could implement a WriteOnly password property:

 Private FPassword As String Public WriteOnly Property Password() As String   Set(ByVal Value As String)     FPassword = Value   End Set End Property 

Note that the code editor only creates a Set block, and the value assigned to the property is stored in an underlying field.

For practical reasons, you can modify the code to be even more secure. For example, you could implement a WriteOnly property that immediately validated the password, in the property setter, and then flushed the variable and only stored whether a valid property was presented but didn't actually store the password. Such a revision might prevent memory sniffer programs from displaying properties stored in the class's memory space.

Other property modifiers, like Shadows, can be used with properties as well as methods. To avoid presenting a significant amount of redundant information in your code, assume that modifiers can be applied to any class member unless the text indicates otherwise . WriteOnly and ReadOnly apply to properties only.

Defining Shared Properties

A comprehensive discussion on Shared members is presented in Chapter 11. Read Chapter 11 for a long discussion of Shared members with examples. For now, suffice it to say, properties can be Shared or instance members. Shared members mean that you can invoke the property on the class, and the property is defined with the Shared keyword. Shared members can also be invoked using instances, but instance members can only be invoked using an instance of the class. Here's an example:

 Public Shared ReadOnly Property Name() As String   Get     Return "PropertyModifiers"   End Get End Property 

The example defines a Public, Shared, ReadOnly property named Name. Because the class name never changes, unless changed in the editor, the method can be read-only. Making the property public means that consumers can query the call name, and using the Shared modifier means that we don't need an object (a class instance) to ask the class "What is your name?" If the class is named PropertyModifiers, we could invoke the getter by writing PropertyModifiers.Name.

The Name property in the fragment demonstrates very specific changes between Visual Basic .NET and VB6. The Name property is very precise in its intent. The difference is that Visual Basic .NET allows us to express intent and enforce it using very precise grammar. We could define a read-only property in VB6 by defining a Let Property statement only, but its read-only status was implicit. However, we couldn't define Shared members.

There may be debate among some developers whether these minor semantics really make a big difference. The answer is that they absolutely do. Programming is almost mathematically precise, and the languages we program in should be explicitly expressive to avoid ambiguous, obtuse, and excessive code. Nuance and subtlety should be left to the language of lovers and statesmen.

Adding Property Attributes

Attributes aren't completely new in Visual Basic .NET. You could actually use a text editor and modify attributes in VB6. To try this, open a VB6 class modulea .CLS filemodify the value of the Attribute VB_PredeclaredId = False attribute statement to Attribute VB_PredeclaredId = True and you will have an autocreated class. Supposing the class name is Class1 with a method Foo: After modifying the attribute statement and importing the text-editor revised class, you could write Class1.Foo and the code would run. This works because the VB_PredeclaredID attribute is the mechanism that makes forms autocreated.

Okay, so why is this point important in a Visual Basic .NET book? Attributes are important because they are a full-fledged aspect of Visual Basic .NET development. Attributes are right out in the open, and they are a hundred times more powerful in Visual Basic .NET. Try the following code revision using the Name property from the last section:

 Public Shared ReadOnly Property Name() As String  <System.Diagnostics.DebuggerHidden()>  Get     Return "PropertyModifiers"   End Get End Property 

Notice the use of the attribute on the getter. (The DebuggerHidden attribute was introduced in Chapter 1.) This attribute prevents the debugger from stepping into this property method. The fragment demonstrates an attribute class defined in the System.Diagnostics namespace.

Attributes can be used for a lot of things in Visual Basic .NET. For example, you use an attribute tag to convert a method into a WebMethod. Attributes are one of the reasons why this isn't your father's Visual Basic. Perhaps attributes may end up being like fast sports cars with adolescent drivers, but they are brand new in Visual Basic .NET and there is enough information that they need their own chapter. (And, of course a fast sports car is a tremendous amount of fun if tempered with experience and diminished testosterone levels.) For now, when you see a < name > tag in Visual Basic .NET code, remember that you are looking at an attribute. Chapter 12 covers attributes in detail.


Team-Fly    
Top
 


Visual BasicR. NET Unleashed
Visual BasicR. NET Unleashed
ISBN: N/A
EAN: N/A
Year: 2001
Pages: 222

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net