Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Professional Visual Studio 2005 (2006) [eng]

.pdf
Скачиваний:
117
Добавлен:
16.08.2013
Размер:
21.9 Mб
Скачать

Chapter 23

method might accidentally be added to this class, which of course could not be called, as an instance of the class could not be created. C# now permits a class to be marked as static, which not only prevents an instance of the class being created, it also prevents any class from inheriting from it and provides design-time checking to ensure that all methods contained in the class are static methods:

public static class HelperMethods

{

static Random rand = new Random();

public static int RandomNumber(int min,int max)

{

return rand.Next(min, max);

}

}

In this code snippet, the static keyword in the first line indicates that this is a static class. As such, it cannot contain instance variables or methods.

Naming Conflicts

An issue that crops up occasionally is how to deal with naming conflicts. Of course, good design practices are one of the best ways to minimize the chance of a conflict. However, this alone is not enough, as quite often you don’t have control over how types in third-party libraries are named. This section covers three techniques to eliminate naming conflicts. To illustrate them, we’ll start from the scenario in which you have a naming conflict for the class BadlyNamedClass in two namespaces:

namespace NamingConflict1

{

public class BadlyNamedClass

{

public static string HelloWorld()

{

return “Hi everyone! - class2”;

}

}

}

namespace NamingConflict2

{

public class BadlyNamedClass

{

public static string HelloWorld()

{

return “Hi everyone! - class1”;

}

}

}

Clearly, if you import both NamingConflict1 and NamingConflict2, you will end up with a naming conflict when you try to reference the class BadlyNamedClass.

306

Language-Specific Features

Namespace Alias Qualifier

When namespaces are imported into a source file with the using statement, the namespaces can be assigned an alias. In addition to minimizing the code you have to write when accessing a type contained within the referenced namespace, providing an alias means that types with the same name in different imported namespaces can be distinguished using the alias. The following example uses a namespace alias to resolve the conflict illustrated in the opener to this section:

using NCF1 = NamingConflict2; using NCF2 = NamingConflict1; public class Naming

{

public static string SayHelloWorldVersion1()

{

return NCF1.BadlyNamedClass.HelloWorld();

}

public static string SayHelloWorldVersion2()

{

return NCF2.BadlyNamedClass.HelloWorld();

}

}

This resolves the current conflict, but what happens when you introduce a class called either NCF1 or NCF2? You end up with a naming conflict between the introduced class and the alias. The namespace alias qualifier :: was added so this conflict could be resolved without changing the alias. To fix the code snippet, you would insert the qualifier whenever you reference NCF1 or NCF2:

using NCF1 = NamingConflict2; using NCF2 = NamingConflict1; public class Naming

{

public static string SayHelloWorldVersion1()

{

return NCF1::BadlyNamedClass.HelloWorld();

}

}

public class NCF1 {/*...*/} public class NCF2 {/*...*/}

The namespace alias qualifier can only be preceded by a using alias (as shown here), the global keyword, or an extern alias (both to be covered in the next sections).

Global

The global identifier is a reference to the global namespace that encompasses all types and namespaces. When used with the namespace alias qualifier, global ensures a full hierarchy match between the referenced type and any imported types. For example, you can modify the sample to use the global identifier:

public class Naming

{

public static string SayHelloWorldVersion1()

{

307

Chapter 23

return global::NamingConflict1.BadlyNamedClass.HelloWorld();

}

}

public class NCF1 {/*...*/} public class NCF2 {/*...*/}

Extern Aliases

Despite both the namespace alias qualifier and the global identifier, it is still possible to introduce conflicts. For example, adding a class called NamingConflict1 would clash with the namespace you were trying to import. An alternative is to use an extern alias. When a reference is added to a project, by default it is assigned to the global namespace and is subsequently available throughout the project. However, this can be modified by assigning the reference to an alternative alias. In Figure 23-1, the conflicting assembly has been assigned an external alias of X.

Figure 23-1

Types and namespaces that exist in references that are added to the global namespace can be used without explicitly importing them into a source file using their fully qualified name. When an alternative alias is specified, as shown in Figure 23-1, this reference must be imported into every source file that needs to use types or namespaces defined within it. This is done with the extern alias statement, as shown in the following example:

extern alias X;

public class Naming

{

public static string SayHelloWorldVersion1()

{

return X::NamingConflict1.BadlyNamedClass.HelloWorld();

}

}

public class NCF1 {/*...*/}

public class NamingConflict1 {/*...*/}

308

Language-Specific Features

This example added a reference to the assembly that contains the NamingConflict1 namespace, and set the Aliases property to X, as shown in Figure 23-1. To reference classes within this assembly, use the namespace alias qualifier, preceded by the extern alias defined at the top of the source file.

Pragma

Occasionally you would like to ignore compile warnings. This can be done for superficial reasons — perhaps you don’t want a warning to appear in the build log. Occasionally, you have a legitimate need to suppress a compile warning; it might be necessary to use a method that has been marked obsolete during a transition phase of a project. Some teams have the compile process set to treat all warnings as errors; in this case, you can use the Pragma statement to disable and then restore warnings:

[Obsolete]

public static string AnOldMethod() { return “Old code....”; }

#pragma warning disable 168

public static string CodeToBeUpgraded()

{

int x;

#pragma warning disable 612 return AnOldMethod(); #pragma warning restore 612

}

#pragma warning restore 168

Two warnings are disabled in this code. The first, warning 168, is raised because you have not used the variable x. The second, warning 612, is raised because you are referencing a method marked with the Obsolete attribute. These warning numbers are very cryptic and your code would benefit from some comments describing each warning and why it is disabled. You might be wondering how you know which warnings you need to disable. The easiest way to determine the warning number is to examine the build output, as shown in Figure 23-2. Here, the warnings CS0612 and CS0168 are highlighted (in yellow), with a description accompanying the warning.

Figure 23-2

VB.NET

Very few new language features are only available in VB.NET, the most significant being the My namespace, which is covered in detail in the next chapter. The other additions are a number of keywords that make the language more powerful and easier to work with.

309

Chapter 23

Continue

The Continue statement is used to move the point of execution to the end of the loop it is contained in, bypassing all remaining code in the block, without prematurely terminating the loop. This is illustrated in the following snippet that prints out the contents of the people list except for any Person with the name Bob:

Public Sub Example()

Dim people As New List(Of Person)

For Each p As Person In people

If p.Name = “Bob” Then Continue For

Console.WriteLine(String.Format(“{0} has {1} relatives”, _

p.Name, p.Relatives.Count))

Next End Sub

The Continue statement can be used to break out of multiple loops. However, as there is no way to label a loop, the loops must be of different types. The following example replaces a second For loop with a Do While loop instead:

Public Sub Example()

Dim people As New List(Of Person) Dim relativeCounter

For Each p As Person In people

If p.Name = “Bob” Then Continue For Console.WriteLine(String.Format(“{0} has {1} relatives”, _

p.Name, p.Relatives.Count))

relativeCounter = 0

While relativeCounter < p.Relatives.Count

If p.Relatives(relativeCounter).Name = “Bob” Then Continue While Console.WriteLine(String.Format(“{0} is a relative of {1}”, _

p.Relatives(relativeCounter).Name, p.Name))

relativeCounter += 1

If relativeCounter = 10 Then Continue For End While

Next End Sub

IsNot

The IsNot operator is the counterpart to the Is operator that is used for reference equality comparisons. Whereas the Is operator will evaluate to True if the references are equal, the IsNot operator will evaluate to True if the references are not equal. Although a minor improvement, this keyword can save a considerable amount of typing, eliminating the need to go back to the beginning of a conditional statement and insert the Not operator:

Dim aPerson As New Person

Dim bPerson As New Person

If Not aPerson Is bPerson Then

Console.WriteLine(“This is the old way of doing this kind of check”) End If

If aPerson IsNot bPerson Then

310

Language-Specific Features

Console.WriteLine(“This is the old way of doing this kind of check”)

End If

Not only does the IsNot operator make it more efficient to write the code; it also makes it easier to read. Instead of the “Yoda-speak” expression If Not aPerson Is bPerson Then, you have the much more readable expression If aPerson IsNot bPerson Then.

Global

The VB.NET Global keyword is very similar to the C# identifier with the same name. Both are used to escape to the outermost namespace, and both are used to remove any ambiguity when resolving namespace and type names. In the following snippet, both Namespace1 and Namespace2 declare a class called AmbiguousClass. The Global keyword is used to distinguish between them:

Imports Namespace1

Imports Namespace2

Public Class Test

Public Sub Example()

Dim x As New Global.Namespace1.AmbiguousClass

Dim y As New Global.Namespace2.AmbiguousClass

End Sub

End Class

TryCast

In an ideal world you would always work with interfaces and there would never be a need to cast between object types. However, the reality is that you build complex applications and often have to break some of the rules of object-orientated programming to get the job done. To this end, one of the most commonly used code snippets is the test-and-cast technique, whereby you test an object to determine whether it is of a certain type before casting it to that type so you can work with it. The problem with this approach is that you are in fact doing two casts, as the TypeOf expression attempts to convert the object to the test type. The result of the conversion is either nothing or an object that matches the test type, so the TypeOf expression then does a check to determine whether the result is nothing. If the result is not nothing, and the conditional statement is true, then the second cast is performed to retrieve the variable that matches the test type. The following example illustrates both the original syntax, using TypeOf, and the improved syntax, using TryCast, for working with objects of unknown type:

Dim fred As Object = New Person If TypeOf (fred) Is Employee Then

Dim emp As Employee = CType(fred, Employee) ‘Do actions with employee

End If

Dim joe As Object = New Person

Dim anotherEmployee As Employee = TryCast(joe, Employee) If anotherEmployee IsNot Nothing Then

‘Do actions with another employee End If

311

Chapter 23

The TryCast expression, as illustrated in the second half of this example, maps directly to the isinst CLR instruction, which will return either nothing or an object that matches the test type. The result can then be compared with nothing before performing operations on the object.

Summar y

This chapter described the features that differentiate C# and VB.NET. Although C# with anonymous methods and iterators is slightly ahead of the game, you are likely to see these differences disappear in the next iteration of the framework and Visual Studio 2005. However, not being able to write anonymous methods and iterators does not limit the code that a VB.NET developer can write. The two primary .NET languages, C# and VB.NET, do have different objectives, but despite their best attempts to differentiate themselves they are constrained by the direction of the .NET Framework itself. In the long run there will be language parity, with differences only in the syntax and the functionality within Visual Studio.

The next chapter looks at the My namespace, which combines a rich class library with a powerful application model to deliver a framework with which developers can truly be more productive.

312

The My Namespace

Many namespaces make up the classes of the .NET Framework. One of the most popular introduced with the release of version 2.0 is the My namespace collection, created specifically to address the needs of Visual Basic programmers who were concerned with the complexity of the original

.NET Framework and how difficult it was to develop Windows applications with Visual Basic

.NET — applications that were previously easy to create in Visual Basic 6.

For clarity, when referring to the entire My feature, it will be referred to as a namespace even though it’s not a true namespace, as you’ll see in the next section.

This, of course, is a concern. After all, creating applications, particularly Windows applications, was meant to be easier and more straightforward with the introduction of the whole fabric of .NET technology components. For Visual Basic programmers, however, many tasks did indeed become more complex and a lot harder to understand. For example, where previously you could use a simple Print command to send a document to the default printer, you now needed to create a whole bunch of objects, and trap events to determine when and what they could print.

When the sheer number of developers clamoring for a solution became obvious, Microsoft began work on providing a way for Visual Basic programmers to achieve results in the efficient manner they were used to without needing to worry about creating whole sets of classes. Thus, the My namespace was born.

This chapter examines the My namespace and describes how you can harness it to simplify the creation of applications. As you’ll see, the My namespace actually encompasses web development as well, bringing the ease of development that Visual Basic 6 programmers were used to in Windows development to web applications and services. Even C# developers can take advantage of My, which can be handy for achieving simple tasks that don’t warrant the extra effort of writing masses of class-based code.

Chapter 24

What Is the My Namespace?

The My feature is not a true namespace like the other parts of the .NET Framework, but it does act just like a namespace in many respects, containing a hierarchy of classes and method definitions that you can utilize in your own applications.

However, My is actually a set of wrapper classes and structures that encapsulate complete sets of .NET classes and automated object instantiations and initializations. The structure of My, shown in Figure 24-1, shows that it is similar to real namespace architecture.

My

My.Application

My.Application.Info

My.Application.Log

My.Computer

My.Computer.Audio

My.Computer.Clipboard

My.Computer.Clock

My.Computer.FileSystem

My.Computer.Info

My.Computer.Keyboard

My.Computer.Mouse

My.Computer.Network

My.Computer.Ports

My.Computer.Registry

My.Forms

My.Log

My.Request

My.Resources

My.Response

My.Settings

My.User

My.WebServices

Figure 24-1

314

The My Namespace

This means that rather than defining a particular object of a system class, instantiating it, initializing it with the values you need, and then using it for the specific purpose you need it for, you can simply refer to the corresponding My class and let .NET work out what needs to happen behind the scenes to achieve the same result. Consider more complex tasks that require you to create up to dozens of classes to achieve something simple, such as establishing user credentials or navigating through the file system efficiently, and compare them to the same one-class access that My provides for such functions and you begin to see the power of what can be achieved.

The Main Components

Ten major classes comprise the top level of My. Each class has a number of methods and properties that you can use in your application, and two of them, My.Application and My.Computer, have additional subordinate classes in the namespace-like structure, which in turn have their own methods and properties. In a moment you’ll see what each of them can do in detail, but here’s a quick reference:

My Object

Purpose

 

 

My.Application

Used to access information about the application, My.Application

 

also exposes certain events that are applicationwide. In addition, this

 

class also has two subordinate My classes: My.Application.Log and

 

My.Application.Info.

My.Computer

Deals with the computer system in which the application is running,

 

and is the most extensive My object. In fact, the My.Computer class has

 

a total of ten subordinate My classes, ranging from

 

My.Computer.Audio to My.Computer.Registry, with classes in

 

between that deal with things such as the file system and the network.

My.Forms

Provides quick access to the forms in the current application project

My.Log

Gives you direct access to the application log so you can interact with

 

it more easily than before

My.Request

Related to web page calls, the My.Request class, along with

 

My.Response and My.WebServices, can be used to simplify your

 

calls and interactions in web-based applications, and is the class used

 

to hold the call to the web service

My.Resources

Allows you to easily access the various resources in your application

My.Response

Holds the web page response. See My.Request for more information.

My.Settings

Used to access both applicationwide and user-specific settings

My.User

Used to determine the user’s current login profile, including security

 

information

My.WebServices

Gives you easy access to all the web services referenced in the current

 

application project

 

 

315