Experience has shown that the best way to develop and maintain a large app is to construct it from small, simple pieces. This technique is called divide and conquer.
Three common ways of packaging code are methods, classes and namespaces.
Methods allow you to modularize an app by separating its tasks into self-contained units.
Dividing an app into meaningful methods makes it easier to debug and maintain.
static
Methods, static
Variables and Class Math
You can call any static
method by specifying the name of the class in which it’s declared, followed by the member-access operator (.
) and the method name, as in
ClassName.MethodName(arguments)
Math
Class MethodsMethod arguments may be constants, variables or expressions.
Math
Class Constants PI
and E
A constant is declared with the keyword const
—its value cannot be changed after the constant is declared.
Math.PI
(3.1415926535897931) is the ratio of a circle’s circumference to its diameter. Math.E
(2.7182818284590451) is the base value for natural logarithms.
When each object of a class maintains its own copy of an attribute, each object (instance) of the class has a separate instance of the variable. When objects of a class containing static
variables are created, all objects of that class share one copy of the class’s static
variables.
Together the static
variables and instance variables represent the fields of a class.
Main
If you declare more than one Main
method among all the classes of your project, you’ll need to indicate which one you would like to be the app’s entry point. You can do this by clicking the menu Project > [ProjectName] Properties... and selecting the class containing the Main
method that should be the entry point from the Startup object list box.
Maximum
Multiple parameters are specified as a comma-separated list.
When a method is called, each parameter is initialized with the value of the corresponding argument. There must be one argument in the method call for each required parameter in the method declaration. Each argument must be consistent with the type of the corresponding parameter.
When program control returns to the point in the app where a method was called, the method’s parameters are no longer accessible.
Methods can return at most one value; the returned value can be a value type that contains many values (implemented as a struct
) or a reference to an object that contains many values.
string
s with ConcatenationC# allows string
objects to be created by assembling smaller string
s into larger string
s using operator +
. This is known as string
concatenation.
Every value of a simple type in C# has a string
representation. When one of the +
operator’s operands is a string
, the other is implicitly converted to a string
, then the two are concatenated.
string
LiteralsAll objects have a ToString
method that returns a string
representation of the object. When an object is concatenated with a string
, the object’s ToString
method is implicitly called to obtain the string
representation of the object, then the two string
s are concatenated.
You’ve seen three ways to call a method—using a method name by itself to call another method of the same class; using a variable that contains a reference to an object, followed by the member-access operator (.
) and the method name to call a non-static
method of the referenced object; and using the class name and the member-access operator (.
) to call a static
method of a class.
A static
method can call only other static
methods of the same class and can manipulate only static
variables in the same class.
You’ve seen three ways to return control to the statement that calls a method—when the method-ending right brace in a method with return type void
is reached, when the following statement executes in a method with return type void
:
return;
and when a method returns a result with a statement of the following form in which the expression is evaluated and its result (and control) are returned to the caller:
return expression;
Another important feature of method calls is argument promotion—implicitly converting an argument’s value to the type that the method expects to receive in its corresponding parameter.
The argument-promotion rules apply to expressions containing values of two or more simple types and to simple-type values passed as arguments to methods.
In cases where information may be lost due to conversion between simple types, the compiler requires you to use a cast operator to explicitly force the conversion.
Many predefined classes are grouped into categories of related classes called namespaces. Together, these namespaces are referred to as the .NET Framework Class Library.
Random
method Next()
generates a random int
value in the range 0 to +2,147,483,646, inclusive.
The methods of class Random
actually generate pseudorandom numbers based on complex mathematical calculations. The calculation that produces the pseudorandom numbers uses the time of day as a seed value to change the sequence’s starting point.
Class Random
provides other versions of method Next
. One receives an int
and returns a value from 0 up to, but not including, the argument’s value. The other receives two int
s and returns a value from the first argument’s value up to, but not including, the second argument’s value.
If the same seed value is used every time, the Random
object produces the same sequence of random numbers. Class Random
’s constructor can receive the seed value as an argument.
enum
Type Status
An enumeration is introduced by the keyword enum
and a type name. Braces delimit the body of an enum
declaration. Inside the braces is a comma-separated list of enumeration constants.
Variables of an enum
type should be assigned only constants of that enum
type.
enum
Type DiceNames
When an enum
is declared, each constant in the enum
declaration is a constant value of type int
. If you do not assign a value to an identifier in the enum
declaration, the compiler will do so. If the first enum
constant is unassigned, the compiler gives it the value 0
. If any other enum
constant is unassigned, the compiler gives it a value equal to one more than the value of the preceding enum
constant. The enum
constant names must be unique, but their underlying values need not be.
enum
If you need to compare a simple integral type value to the underlying value of an enumeration constant, you must use a cast operator to make the two types match.
The scope of a declaration is the portion of the app that can refer to the declared entity by its unqualified name.
The scope of a parameter declaration is the body of the method in which the declaration appears.
A local variable’s scope is from the point at which the declaration appears to the end of that block.
The scope of a local-variable declaration that appears in the initialization section of a for
statement’s header is the body of the for
statement and the other expressions in the header.
The scope of a method, property or field of a class is the entire body of the class.
Any block may contain variable declarations. If a local variable or parameter in a method has the same name as a field, the field is hidden until the block terminates execution.
Stacks are known as last-in, first-out (LIFO) data structures—the last item pushed (inserted) on the stack is the first item popped off (removed from) the stack.
The method-call stack (sometimes referred to as the program-execution stack) is a data structure that supports the method call/return mechanism. It also supports the creation, maintenance and destruction of each called method’s local variables.
Each method eventually must return control to the method that called it. The method-call stack is the perfect data structure for handling this information.
Each time a method calls another method, an entry (called a stack frame or an activation record) is pushed onto the stack. This entry contains the return address that the called method needs in order to return to the calling method.
If a called method returns instead of calling another method before returning, the stack frame for the method call is popped, and control transfers to the return address in the popped stack frame.
The same techniques apply when a method accesses a property or when a property calls a method.
Stack frames also contain the memory for parameters and any local variables a method declares.
When a method returns—and no longer needs its local variables—its stack frame is popped from the stack, and those local variables no longer exist.
If more method calls occur than can have their activation records stored on the method-call stack, a fatal error known as stack overflow occurs.
Methods of the same name can be declared in the same class, as long as they have different sets of parameters. This is called method overloading. When an overloaded method is called, the C# compiler selects the appropriate method by examining the number, types and order of the arguments in the call.
The compiler distinguishes overloaded methods by their signature—a combination of the method’s name and the number, types and order of its parameters. The signature also includes the way those parameters are passed, which can be modified by the ref
and out
keywords.
The compiler will generate an error when two methods have the same signature but different return types. Overloaded methods can have the same or different return types if the methods have different parameter lists.
Methods can have optional parameters, which allow the calling method to vary the number of arguments to pass. An optional parameter specifies a default value that’s assigned to the parameter if the optional argument is omitted.
Methods can have one or more optional parameters. All optional parameters must be placed to the right of the method’s non-optional parameters.
When a parameter has a default value, the caller has the option of passing that particular argument.
Normally, when calling a method, the argument values—in order—are assigned to the parameters from left to right in the parameter list.
C# provides a feature called named parameters, which enable you to call methods that receive optional parameters by providing only the optional arguments you wish to specify. To do so, you explicitly specify the parameter’s name and value—separated by a colon (:
)—in the argument list of the method call.
C# 6 introduces a new expression-bodied method syntax for methods that contain only a return
statement that returns a value or a single-statement body, and a new expression-bodied property syntax for read-only properties in which the get
accessor contains only a return
statement.
In a method, the symbol => follows the method’s parameter list and introduces the body—no braces or return statement are required.
A read-only property can be implemented as an expression-bodied property with the syntax
modifiers returnType PropertyName => expression;
A recursive method calls itself, either directly or indirectly through another method.
When a recursive method is called to solve a problem, the method actually is capable of solving only the simplest case(s), or base case(s). If the method is called with a base case, the method returns a result.
If the method is called with a more complex problem, the method divides the problem into two conceptual pieces: a piece that the method knows how to do and a piece that it does not know how to do. Because this new problem looks like the original problem, the method calls a fresh copy of itself to work on the smaller problem; this procedure is referred to as a recursive call and is also called the recursion step.
A recursive declaration of the factorial method is arrived at by observing the relationship:
n! = n · (n – 1)!
Types in C# are divided into value types and reference types.
C#’s simple types (like int
), enum
types and struct
types are all value types.
A variable of a value type simply contains a value of that type.
A reference-type variable (sometimes called a reference) contains the location where the data referred to by that variable is stored. Such a variable is said to refer to an object.
Reference-type instance variables are initialized by default to null
.
Type string
is a reference type. A string
variable with the value null
is not an empty string
, which is represented by ""
or string.Empty
.
The value null
represents a reference that does not refer to an object.
The empty string
is a string
object that does not contain any characters.
Two ways to pass arguments to methods in many programming languages are pass-by-value and pass-by-reference.
When an argument is passed by value (the default), a copy of the argument’s value is passed to the called method. Changes to the copy do not affect the original variable’s value in the caller.
When an argument is passed by reference, the caller gives the method the ability to access and modify the caller’s original data directly.
Value-type variables store values, so specifying a value-type variable in a method call passes a copy of that variable’s value to the method. Reference-type variables store references to objects, so specifying a reference-type variable as an argument passes the method a copy of the actual reference that refers to the object.
When returning information from a method via a return
statement, the method returns a copy of the value stored in a value-type variable or a copy of the reference stored in a reference-type variable.
ref
and out
ParametersC# provides the keywords ref
and out
to pass variables by reference.
A ref
parameter indicates that an argument will be passed to the method by reference—the called method will be able to modify the original variable in the caller.
An out
parameter indicates that a possibly uninitialized variable will be passed into the method by reference and that the called method will assign a value to the original variable in the caller.
A method can return only one value to its caller via a return statement, but can return many values by specifying multiple output (ref
and/or out
) parameters.
When a variable is passed to a method with a reference parameter, you must precede the variable with the same keyword (ref
or out
) that was used to declare the reference parameter.