Home Random Page


CATEGORIES:

BiologyChemistryConstructionCultureEcologyEconomyElectronicsFinanceGeographyHistoryInformaticsLawMathematicsMechanicsMedicineOtherPedagogyPhilosophyPhysicsPolicyPsychologySociologySportTourism






Nbsp;   Namespaces and Assemblies

Namespaces allow for the logical grouping of related types, and developers typically use them to make it easier to locate a particular type. For example, the System.Text namespace defines a bunch of types for performing string manipulations, and the System.IO namespace defines a bunch of types for performing I/O operations. Here’s some code that constructs a System.IO.FileStream object and a System.Text.StringBuilder object.

 

public sealed class Program { public static void Main() {

System.IO.FileStream fs = new System.IO.FileStream(...); System.Text.StringBuilder sb = new System.Text.StringBuilder();

}

}


As you can see, the code is pretty verbose; it would be nice if there were some shorthand way to refer to the FileStream and StringBuilder types to reduce typing. Fortunately, many compilers do offer mechanisms to reduce programmer typing. The C# compiler provides this mechanism via the using directive. The following code is identical to the previous example.

 

using System.IO; // Try prepending "System.IO." using System.Text; // Try prepending "System.Text."

 

public sealed class Program { public static void Main() {

FileStream fs = new FileStream(...); StringBuilder sb = new StringBuilder();

}

}

 

To the compiler, a namespace is simply an easy way of making a type’s name longer and more likely to be unique by preceding the name with some symbols separated by dots. So the compiler interprets the reference to FileStream in this example to mean System.IO.FileStream. Similarly, the compiler interprets the reference to StringBuilder to mean System.Text.StringBuilder.

Using the C# using directive is entirely optional; you’re always welcome to type out the fully qualified name of a type if you prefer. The C# using directive instructs the compiler to try prepend- ing different prefixes to a type name until a match is found.

       
   
 
 

 

In the previous code example, the compiler needs to ensure that every type referenced exists and that my code is using that type in the correct way: calling methods that exist, passing the right number of arguments to these methods, ensuring that the arguments are the right type, using the

method’s return value correctly, and so on. If the compiler can’t find a type with the specified name in the source files or in any referenced assemblies, it prepends System.IO. to the type name and checks if the generated name matches an existing type. If the compiler still can’t find a match, it prepends System.Text. to the type’s name. The two using directives shown earlier allow me to simply type FileStream and StringBuilder in my code—the compiler automatically expands the references to System.IO.FileStream and System.Text.StringBuilder. I’m sure you can easily imagine how much typing this saves, as well as how much cleaner your code is to read.

When checking for a type’s definition, the compiler must be told which assemblies to examine by using the /reference compiler switch as discussed in Chapter 2, “Building, Packaging, Deploying, and Administering Applications and Types,” and Chapter 3, “Shared Assemblies and Strongly Named Assemblies.” The compiler will scan all of the referenced assemblies looking for the type’s definition.




After the compiler finds the proper assembly, the assembly information and the type information is emitted into the resulting managed module’s metadata. To get the assembly information, you must pass the assembly that defines any referenced types to the compiler. The C# compiler, by default, auto- matically looks in the MSCorLib.dll assembly even if you don’t explicitly tell it to. The MSCorLib.dll as- sembly contains the definitions of all of the core Framework Class Library (FCL) types, such as Object, Int32, String, and so on.

As you might imagine, there are some potential problems with the way that compilers treat namespaces: it’s possible to have two (or more) types with the same name in different namespaces. Microsoft strongly recommends that you define unique names for types. However, in some cases, it’s simply not possible. The runtime encourages the reuse of components. Your application might take advantage of a component that Microsoft created and another component that Wintellect created. These two companies might both offer a type called Widget—Microsoft’s Widget does one thing, and Wintellect’s Widget does something entirely different. In this scenario, you had no control over the naming of the types, so you can differentiate between the two widgets by using their fully qualified names when referencing them. To reference Microsoft’s Widget, you would use Microsoft.Widget, and to reference Wintellect’s Widget, you would use Wintellect.Widget. In the following code,

the reference to Widget is ambiguous, so the C# compiler generates the following message: error CS0104: 'Widget' is an ambiguous reference between 'Microsoft.Widget' and 'Wintel­ lect.Widget'.

 

using Microsoft; // Try prepending "Microsoft." using Wintellect; // Try prepending "Wintellect."

 

public sealed class Program { public static void Main() {

Widget w = new Widget();// An ambiguous reference

}

}

 

To remove the ambiguity, you must explicitly tell the compiler which Widget you want to create.

 

using Microsoft; // Try prepending "Microsoft." using Wintellect; // Try prepending "Wintellect."

 

public sealed class Program { public static void Main() {

Wintellect.Widget w = new Wintellect.Widget(); // Not ambiguous

}

}

 

There’s another form of the C# using directive that allows you to create an alias for a single type or namespace. This is handy if you have just a few types that you use from a namespace and don’t want to pollute the global namespace with all of a namespace’s types. The following code demon- strates another way to solve the ambiguity problem shown in the preceding code.

 

using Microsoft; // Try prepending "Microsoft." using Wintellect; // Try prepending "Wintellect."


// Define WintellectWidget symbol as an alias to Wintellect.Widget using WintellectWidget = Wintellect.Widget;

 

public sealed class Program { public static void Main() {

WintellectWidget w = new WintellectWidget(); // No error now

}

}

 

These methods of disambiguating a type are useful, but in some scenarios, you need to go further. Imagine that the Australian Boomerang Company (ABC) and the Alaskan Boat Corporation (ABC) are each creating a type, called BuyProduct, which they intend to ship in their respective assemblies. It’s likely that both companies would create a namespace called ABC that contains a type called Buy­ Product. Anyone who tries to develop an application that needs to buy both boomerangs and boats would be in for some trouble unless the programming language provides a way to programmatically distinguish between the assemblies, not just between the namespaces. Fortunately, the C# compiler offers a feature called extern aliases that gives you a way to work around this rarely occurring prob- lem. Extern aliases also give you a way to access a single type from two (or more) different versions of the same assembly. For more information about extern aliases, see the C# Language Specification.

In your library, when you’re designing types that you expect third parties to use, you should define these types in a namespace so that compilers can easily disambiguate them. In fact, to reduce the likelihood of conflict, you should use your full company name (not an acronym or abbreviation) to be your top-level namespace name. Referring to the Microsoft .NET Framework SDK documen- tation, you can see that Microsoft uses a namespace of “Microsoft” for Microsoft-specific types.

(See the Microsoft.CSharp, Microsoft.VisualBasic, and Microsoft.Win32 namespaces as examples.)

Creating a namespace is simply a matter of writing a namespace declaration into your code as fol- lows (in C#).

 

namespace CompanyName {

public sealed class A { // TypeDef: CompanyName.A

}

 

namespace X {

public sealed class B { ... } // TypeDef: CompanyName.X.B

}

}

 

The comment on the right of the preceding class definitions indicates the real name of the type the compiler will emit into the type definition metadata table; this is the real name of the type from the CLR’s perspective.

Some compilers don’t support namespaces at all, and other compilers are free to define what “namespace” means to a particular language. In C#, the namespace directive simply tells the compiler to prefix each type name that appears in source code with the namespace name so that programmers can do less typing.


 


Date: 2016-03-03; view: 626


<== previous page | next page ==>
Nbsp;   Casting Between Types | Nbsp;   How Things Relate at Run Time
doclecture.net - lectures - 2014-2024 year. Copyright infringement or personal data (0.008 sec.)