Reference types, Value types, and Patterns |
A complete PDF version of the text book is now available. The PDF version is an almost complete subset of the HTML version (where only a few, long program listings have been removed). See here. |
This chapter is of a different nature than the previous chapters.
At this point you are supposed to be able to program simple classes, like the Die class in Program 10.1, the Point class in Program 11.2, and the BankAccount class in Program 11.5. Eventually, it will be necessary to care about how classes are organized in relation to each other. We chose to cover C# program organization now. In case you are not motivated for these issues, you can skip the chapter at this point in time. But you are advised to come back to it before you start writing large C# programs.
If you want to read more about the organization of C# programs, you are recommended to study chapter 16 of C# Language Specification [ECMA-334].
We show a lot of examples in this chapter. In the web edition, all examples are present. In the paper edition, only the most fundamental examples appear. Therefore, if you want to understand all the details of this chapter, read the web edition.
|
15.1. Program Organization
Contents Up Previous Next Slide Annotated slide Aggregated slides Subject index Program index Exercise index
The structure and organization of a C# program is radically different from the structure and organization of both C programs and Java programs. Below we emphasize some important observations about the organization of C# programs.
|
As noticed above, a single namespace can be spread out on several source files. In Section 11.12 we have also seen that a single class - called a partial class - can be defined in two or more source files.
15.2. Examples of Program Organization
Contents Up Previous Next Slide Annotated slide Aggregated slides Subject index Program index Exercise index
In the majority of the small programs, which we present in this material, namespaces do not appear explicitly. Most of the programs we have shown until now in this material follow the pattern of Program 15.1.
1 2 3 4 5 6 7 8 9 10 11 12 13 | // The most simple organization // A class located directly in the global namespace // In source file ex.cs using System; public class C { public static void Main(){ Console.WriteLine("The start of the program"); } } | |||
|
In Program 15.1 the class C is a member of the (implicitly stated) global name space. The compilation of the program in Program 15.1 can be done as shown in Listing 15.2 (only on web).
1 2 3 4 5 | In Windows SDK csc ex.cs In Mono gmcs ex.cs | |||
|
Below, in Program 15.3 the namespaces N1 and N2 are members of the global name space. N2 contains a nested namespace called N3.
You should use namespaces to group types that somehow belong together, either conceptually or according the architecture of the software you are creating. Namespaces are also useful if you have identically named types (such as two classes with the same name) that should coexist. In that case, place the conflicting types in different namespaces, and be sure to use the involved namespaces with qualified access - "namespace dotting". Use of several namespaces, such as N1, N2, and N3 in Program 15.3 is, in general, relevant only in large programs with many types.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | // Several namespaces, including nested namespaces. // In source file ex.cs namespace N1 { public class C1{}; } namespace N2 { internal class C2{}; public class C3{}; namespace N3 { public class C4{ C2 v; } } } | |||
|
In Program 15.4 we show how to use the classes C1, C2, C3, and C4 from Program 15.3. The using directives import the types of a namespace. Importing a namespace N implies that types T in N can be used without qualification. Thus, we can write T instead of N.T. The three using directives in line 15-17 of Program 15.4 open up the namespaces N1, N2 and N2.N3. If the namespaces in Program 15.3 and the client class shown in Program 15.4 are compiled to two different assemblies (dll files) then C2 cannot be used in the Client class. The reason is that C2 is internal in its assembly.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | // A client program // In source file client.cs /* Namespace N1 public class C1 Namespace N2 internal class C2 public class C3 Namespace N3 public class C4 */ using N1; using N2; using N2.N3; public class Client{ C1 v = new C1(); // The type or namespace name 'C2' could not be found. // C2 w = new C2(); C3 x = new C3(); C4 y = new C4(); } | |||
|
If you avoid the using directives, you are punished with the need to use a lot of "namespace dotting". If you wish to see the effect of this, please consult Program 15.5 (only on web)
The compilation of Program 15.3 together with Program 15.5 and Program 15.4 (only on web) is shown in Listing 15.6 (only on web).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | // An equivalent client program. No using clauses. // In source file client-equiv.cs /* Namespace N1 public class C1 Namespace N2 internal class C2 public class C3 Namespace N3 public class C4 */ // No using clauses - instead fully qualified class names: public class Client{ N1.C1 v = new N1.C1(); // The type or namespace name 'C2' could not be found. // N2.C2 w = new N2.C2(); N2.C3 x = new N2.C3(); N2.N3.C4 y = new N2.N3.C4(); } | |||
|
1 2 3 4 5 6 7 8 9 | In Windows SDK csc /target:library ex.cs csc /target:library /reference:ex.dll client.cs csc /target:library /reference:ex.dll client-equiv.cs In MONO gmcs /target:library ex.cs gmcs /target:library /reference:ex.dll client.cs gmcs /target:library /reference:ex.dll client-equiv.cs | |||
|
Nested namespaces can be given by textual nesting, as shown in Program 15.3 or in Program 15.7 (only on web). Alternatively, it can be given as shown in Program 15.8. In Program 15.8 the namespaces N2 and N3 are both member of the namespace N1. Thus, the situation in Program 15.8 is identical to the situation shown in Program 15.7 (only on web).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | // Again nested namespaces. N1 contains N2 and N3. // In source file ex.cs namespace N1 { namespace N2 { public class C1{}; public class C2{}; } namespace N3 { public class C3{} } } | |||
|
1 2 3 4 5 6 7 8 9 10 11 12 | // Equivalent to the previous program // No physical namespace nesting // In source file ex-equiv.cs namespace N1.N2 { public class C1{}; public class C2{}; } namespace N1.N3 { public class C3{} } | |||
|
The classes C1, C2, and C3 of either Program 15.8 or Program 15.7 (only on web) can be used in a Client class, as shown in Program 15.9 (only on web). The compilation can be done as shown in Listing 15.10 (only on web).
1 2 3 4 5 6 7 8 9 10 | // In source file client.cs using N1.N2; using N1.N3; public class Client{ C1 x = new C1(); C2 y = new C2(); C3 z = new C3(); } | |||
|
1 2 3 4 5 6 | In Windows SDK csc /target:library ex.cs csc /target:library /reference:ex.dll client.cs csc /t:library ex-equiv.cs csc /t:library /r:ex-equiv.dll /out:client-equiv.dll client.cs | |||
|
A namespace, such as Intro in Program 15.11 is open ended in the sense that stuff can be added to Intro from another source file. Both Program 15.11 and Program 15.12 contribute to the Intro namespace. Thus, when the two source files are taken together, Intro contains the types A, B, and C. The use of the namespace Intro is shown in Client class in Program 15.13 (only on web). In Listing 15.14 (only on web) we show how to compile the two source files f1.cs and f2.cs behind the namespace Intro together.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | // f1.cs: First part of the namespace Intro using System; namespace Intro{ internal class A { public void MethA (){ Console.WriteLine("This is MethA in class Intro.A"); } } public class B { private A var = new A(); public void MethB (){ Console.WriteLine("This is MethB in class Intro.B"); Console.WriteLine("{0}", var); } } } | |||
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | // f2.cs: Second part of the namespace Intro using System; namespace Intro{ public class C { private A var1 = new A(); private B var = new B(); public void MethC (){ Console.WriteLine("This is MethC in class Intro.C"); Console.WriteLine("{0}", var); Console.WriteLine("{0}", var1); } } } | |||
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | // client.cs: A client of the classes in namespace Intro /* namespace Intro internal class A public class B public class C */ using System; using Intro; class Client { public static void Main(){ // A a = new A(); // Compiler error: Class A is internal in Intro B b = new B(); C c = new C(); Console.WriteLine("{0} {1}", b, c); } } | |||
|
The problem reported in line 18 of Program 15.13 relies on the compilation of the program to two different assemblies, as shown in Listing 15.14. If both the Intro namespace and the Client class are compiled to a single assembly there will be no error in line 18.
1 2 3 4 5 6 7 8 9 10 11 12 | In Windows SDK csc /target:library /out:assembly.dll f1.cs f2.cs csc /reference:assembly.dll client.cs client.exe In MONO: gmcs /target:library /out:assembly.dll f1.cs f2.cs gmcs /reference:assembly.dll client.cs mono client.exe | |||
|
The compilations shown in Listing 15.14 illustrate how to compile the files f1.cs and f2.cs together. In general, it is possible to compile a number of C# source files together as though these source files were contained in a single large source file. This way of compilation is often an easy way to compile a number of C# source files that depend on each other in circular ways. Alternatively, each file must be compiled in isolation and in a particular order, with use of the reference compiler option.
Notice also, from Listing 15.14, that you can control the name of the assembly via use of the out compiler option.
15.3. Namespaces and Visibility
Contents Up Previous Next Slide Annotated slide Aggregated slides Subject index Program index Exercise index
In this section we summarize the visibility rules of types and namespaces, both of which can occur in (other) namespaces.
|
You should pay attention to the default visibility of types in namespaces. If you do not give a visibility modifier of a type T (a class, for instance) in a namespace N, T is internal in N. This may lead to surprises if you in reality forgot to state that T should have been public. We have already discussed this in Section 11.16.
15.4. Namespaces and Assemblies
Contents Up Previous Next Slide Annotated slide Aggregated slides Subject index Program index Exercise index
|
The file/directory organization, the namespace/class organization and the assembly organization are relatively independent of each other |
15.5. References
[Ecma-334] | "The C# Language Specification", June 2005. ECMA-334. |