using System; using System.Reflection; [assembly:AssemblyKeyFile("MyPublicKey.snk")] [assembly:AssemblyCulture("")] [assembly:AssemblyVersion("1.0.0.0")] [assembly:AssemblyDelaySign(true)] public class HelloWorld { public static void Main() { Console.WriteLine("Hello, world"); } }
Delay Sign an Assembly in C#
Verify That a Strong-Named Assembly Has Not Been Modified in C#
Microsoft (R) .NET Framework Strong Name Utility Version 1.1.4322.573Copyright (C) Microsoft Corporation 1998-2002. All rights reserved.Assembly 'HelloWorld.exe' is valid
Microsoft (R) .NET Framework Strong Name Utility Version 1.1.4322.573Copyright (C) Microsoft Corporation 1998-2002. All rights reserved.Failed to verify assembly -- Unable to format error message 8013141A
Give an Assembly a Strong Name in C#
-
A strong name key pair contained either in a file or in a CSP key container.
-
To use assembly-level attributes to specify the location where the compiler can obtain your strong name key pair.
-
If your key pair is in a file, apply the attribute System.Reflection.AssemblyKeyFileAttribute to your assembly and specify the name of the file that contains the keys.
-
If your key pair is in a CSP container, apply the attribute System.Reflection.AssemblyKeyNameAttribute to your assembly and specify the name of the container in which the keys are stored.
-
-
Specify the culture that your assembly supports by applying the attribute System.Reflection.AssemblyCultureAttribute to the assembly. (You can't specify a culture for executable assemblies because executable assemblies support only the neutral culture.)
-
Specify the version of your assembly by applying the attribute System.Reflection.AssemblyVersionAttribute to the assembly.
using System;using System.Reflection;[assembly:AssemblyKeyName("MyKeys")][assembly:AssemblyCulture("")][assembly:AssemblyVersion("1.0.0.0")]public class HelloWorld {public static void Main() {Console.WriteLine("Hello, world");}}
Note: | You can also build strong-named assemblies using the Assembly Linker (al.exe), which allows you to specify the strong name information on the command line instead of using attributes in your code. This is useful when you don't want to embed the strong name attributes in your source file and when you use scripts to build large source trees. Refer to the Assembly Linker information in the .NET Framework SDK documentation for more details. |
Create and Manage Strong-Named Key Pairs in C#
Microsoft (R) .NET Framework Strong Name Utility Version 1.1.4322.573Copyright (C) Microsoft Corporation 1998-2002. All rights reserved.Public key is07020000002400005253413200040000010001002b4ef3c2bbd6478802b64d0dd3f2e7c65ee;<$VE>6478802b63cb894a782f3a1adbb46d3ee5ec5577e7dccc818937e964cbe997c12076c19f2d7ad179f15f7dccca6c6b72aPublic key token is 2a1d3326445fc02a
-
The structure and size of your organization.
-
Your development and release process.
-
The software and hardware resources you have available.
-
The requirements of your customer base.
Tip Commonly, a small group of trusted individuals (the signing authority) has responsibility for the security of your company's strong name signing keys and is responsible for signing all assemblies just prior to their final release. The ability to delay sign an assembly facilitates this model and avoids the need to distribute private keys to all development team members.
Access a Program Element That Has the Same Name as a Keyword in C#
// Instantiate an operator object@operator Operator1 = new @operator();// Set the operator's volatile propertyOperator1.@volatile = true;
Selectively Include Code at Build Time in C#
#define win2000#define release#undef win98using System;public class ConditionalExample {public static void Main() {// Declare a string to contain the platform namestring platformName;#if winXP // Compiling for Windows XPplatformName = "Microsoft Windows XP";#elif win2000 // Compiling for Windows 2000platformName = "Microsoft Windows 2000";#elif winNT // Compiling for Windows NTplatformName = "Microsoft Windows NT";#elif win98 // Compiling for Windows 98platformName = "Microsoft Windows 98";#else // Unknown platform specifiedplatformName = "Unknown";#endifConsole.WriteLine(platformName);}}
Operator | Example | Description |
---|---|---|
== | #if winXP == true | Equality. Evaluates to true if the symbol winXP is defined. Equivalent to #if winXP. |
!= | #if winXP != true | Inequality. Evaluates to true if the symbol winXP is not defined. Equivalent to #if !winXP. |
&& | #if winXP && release | Logical AND. Evaluates to true only if the symbols winXP AND release are defined. |
|| | #if winXP || release | Logical OR. Evaluates to true if either of the symbols winXP OR release are defined. |
() | #if (winXP || win2000) && release | Parentheses allow you to group expressions. Evaluates to true if the symbols winXP OR win2000 are defined AND the symbol release is defined. |
Warning | You must be careful not to overuse conditional compilation directives and not to make your conditional expressions too complex; otherwise, your code can quickly become confusing and unmanageable—especially as your projects become larger. |
[System.Diagnostics.Conditional("DEBUG")]public static void DumpState() {//…}
[System.Diagnostics.Conditional("DEBUG")][System.Diagnostics.Conditional("TEST")]public static void DumpState() {//…}
[System.Diagnostics.Conditional("DEBUG")]public static void DumpState() {DumpState2();}[System.Diagnostics.Conditional("TEST")]public static void DumpState2() {//…}
Note | The Debug and Trace classes from the System.Diagnostics namespace use ConditionalAttribute on many of their methods. The methods of the Debug class are conditional on the definition of the symbol DEBUG, and the methods of the Trace class are conditional on the definition of the symbol TRACE. |
Access Command-Line Arguments in C#
public static void Main(string[] args) {}public static int Main(string[] args) {}
public class CmdLineArgExample {public static void Main(string[] args) {// Step through the command-line argumentsforeach (string s in args) { System.Console.WriteLine(s);}}}
CmdLineArgExample "one \"two\" three" four 'five six'
one "two" threefour'fivesix'
Create and Use a Code Library in C#
-
If you reference more than one library, separate each library name with a comma or semicolon, but no spaces. For example, /reference:ConsoleUtils.dll,WindowsUtils.dll.
-
If the libraries aren't in the same directory as the source code, use the /lib switch on the compiler to specify the additional directories where the compiler should look for libraries. For example, /lib:c:\CommonLibraries,c:\Dev\ThirdPartyLibs.
-
If the library you need to reference is a multi-file assembly, reference the file that contains the assembly manifest. (For information about multi-file assemblies, see recipe 1.3.)
Create and Use a Code Module in C#
-
Microsoft Intermediate Language (MSIL) code created from your C# source code during compilation
-
Metadata describing the types contained in the module
-
Resources, such as icons and string tables, used by the types in the module
-
The runtime will load a module only when the types defined in the module are required. Therefore, where you have a set of types that your application uses rarely, you can partition them into a separate module that the runtime will load only if necessary. This offers the following benefits:
-
Improved performance, especially if your application is loaded across a network.
-
Minimizing the use of memory.
-
-
The ability to use many different languages to write applications that run on the common language runtime (CLR) is a great strength of the .NET Framework. However, the C# compiler can't compile your Microsoft Visual Basic .NET or COBOL .NET code for inclusion in your assembly. You must first use a language-specific compiler to turn your source into MSIL in a structure that the C# compiler can incorporate—a module. Likewise, if you want to allow programmers of other languages to use the types you develop in C#, you must build them into a module.
-
MyFirstApp.exe, which contains the assembly manifest as well as the MSIL for the types declared in the SourceOne.cs and SourceTwo source files.
-
ConsoleUtils.netmodule and WindowsUtils.netmodule, which are now integral components of the multi-file assembly but are unchanged by this compilation process. (If you attempt to run MyFirstApp.exe without the netmodules present, a System.IO.FileNotFoundException is thrown.)
Short-Circuit Logical Operators in C#
// Demonstrate the short-circuit operators, using System; class SCops { public static void Main() { int n, d; n = 10; d = 2; if(d != 0 && (n % d) == 0) Console.WriteLine(d + " is a factor of " + n); d = 0; // now, set d to zero // Since d is zero, the second operand is not evaluated, if(d != 0 && (n % d) == 0) Console.WriteLine(d + " is a factor of " + n); /* Now, try the same thing without short-circuit operator. This will cause a divide-by-zero error. */ if(d != 0 & (n % d) == 0) Console.WriteLine(d + " is a factor of " + n); } }
// Side effects can be important, using System; class SideEffects { public static void Main() { int i; bool someCondition = false; i = 0; /* Here, i is still incremented even though the if statement fails. */ if(someCondition & (++i < 100)) Console.WriteLine("this won't be displayed"); Console.WriteLine("if statement executed: " + i); // displays 1 /* In this case, i is not incremented because the short-circuit operator skips the increment. */ if(someCondition && (++i < 100)) Console.WriteLine("this won't be displayed"); Console.WriteLine("if statement executed: " + i); // still 1 !! } }
Relational and Logical Operators in C#
Operator | Meaning |
---|---|
= = | Equal to |
!= | Not equal to |
> | Greater than |
< | Less than |
>= | Greater than or equal to |
<= | Less than or equal to |
Operator | Meaning |
---|---|
& | AND |
| | OR |
^ | XOR (exclusive OR) |
|| | Short-circuit OR |
&& | Short-circuit AND |
! | NOT |
p | q | p&q | p|q | p^q | !p |
---|---|---|---|---|---|
False | False | False | False | False | True |
True | False | False | True | True | False |
False | True | False | True | True | True |
True | True | True | True | False | False |
// Demonstrate the relational and logical operators, using System; class RelLogOps { public static void Main() { int i, j; bool b1, b2; i = 10; j = 11; if(i < j) Console.WriteLine("i < j"); if(i <= j) Console.WriteLine("i <= j"); if(i != j) Console.WriteLine("i != j"); if(i == j) Console.WriteLine("this won't execute"); if(i >= j) Console.WriteLine("this won't execute"); if(i > j) Console.WriteLine("this won't execute"); b1 = true; b2 = false; if(b1 & b2) Console.WriteLine("this won't execute"); if(!(b1 & b2)) Console.WriteLine("!(b1 & b2) is true"); if(b1 b2) Console.WriteLine("b1 | b2 is true"); if(b1 ^ b2) Console.WriteLine("b1 ^ b2 is true"); } }
p | q | p implies q |
---|---|---|
True | True | True |
True | False | False |
False | False | True |
False | True | True |
!p | q
// Create an implication operator in C#. using System; class Implication { public static void Main() { bool p=false, q=false; int i, j; for(i =0; i < 2; i++) { for(j = 0; j < 2; j++) { if (i==0) p = true; if (i==1) p = false; if (j==0) q = true; if (j==1) q = false; Console .WriteLine ("p is " + p + '' q is " + q); if(!p | q) Console.WriteLine(p + implies " + q + " is " + true); Console.WriteLine(); } } } }
p is True, q is True True implies True is True p is True, q is False p is False, q is True False implies True is True p is False, q is False False implies False is True
Arithmetic Operators in C#
Operator | Meaning |
---|---|
+ | ; Addition |
− | Subtraction (also unary minus) |
* | Multiplication |
/ | Division |
% | Modulus |
++ | Increment |
−− | Decrement |
// Demonstrate the % operator, using System; class ModDemo { public static void Main() { int iresult, irem; double dresult, drem; iresult = 10 / 3; irem = 10 % 3; dresult = 10.0 / 3.0; drem = 10.0 % 3.0; Console.WriteLine("Result and remainder of 10 / 3: " + iresult + " " + irem); Console.WriteLine("Result and remainder of 10.0 / 3.0: " + dresult + " " + drem); } }
Result and remainder of 10 / 3: 3 1 Result and remainder of 10.0 / 3.0: 3.33333333333333 1
Increment and Decrement
x = x + 1;
x++;
x = x − 1;
x = x + 1;
++x; // prefix form
x++; // postfix form
x = 10; y = ++x;
x = 10; y = x++;
/* Demonstrate the difference between prefix postfix forms of ++. */ using System; class PrePostDemo { public static void Main() { int x, y; int i; x = 1; Console.WriteLine("Series generated using y = x + x++;"); for(i = 0; i < 10; i++) { y = x + x++; // postfix ++ Console.WriteLine(y + " "); } Console.WriteLine(); x = 1; Console.WriteLine("Series generated using y = x + ++x;"); for(i = 0; i < 10; i++) { y = x + ++x; // prefix ++ Console.WriteLine(y + " "); } Console.WriteLine(); } }
Series generated using y = x + x++; 2 4 6 8 10 12 14 16 18 20 Series generated using y = x + ++x; 3 5 7 9 11 13 15 17 19 21
y = x + x++;
y = x + ++x;
x + ++x
Type Conversion of Expressions in C#
IF one operand is a decimal, THEN the other operand is promoted to decimal (unless it is of type float or double, in which case an error results).
ELSE IF one operand is a double, the second is promoted to double.
ELSE IF one operand is a float, the second is promoted to float.
ELSE IF one operand is a long, the second is promoted to long.
ELSE IF one operand is a uint and the second is of type sbyte, short, or int, both are promoted to long.
ELSE IF one operand is a uint, the second is promoted to uint.
ELSE both operands are promoted to int.
// A promotion surprise! using System; class PromDemo { public static void Main() { byte b; b = 10; b = (byte) (b * b); // cast needed!! Console.WriteLine("b: "+ b); } }
char ch1 = 'a', ch2 = 'b'; ch1 = (char) (ch1 + ch2);
Using Casts in Expressions
// Using casts in an expression. using System; class CastExpr { public static void Main() { double n; for(n = 1.0; n <= 10; n++) { Console.WriteLine("The square root of {0} is {1}", n, Math.Sqrt(n)); Console.WriteLine("Whole number part: {0}" , (int) Math.Sqrt(n)); Console.WriteLine("Fractional part: {0}", Math.Sqrt(n) - (int) Math.Sqrt(n) ); Console.WriteLine(); } } }
The square root of 1 is 1 Whole number part: 1 Fractional part: 0 The square root of 2 is 1.4142135623731 Whole number part: 1 Fractional part: 0.414213562373095 The square root of 3 is 1.73205080756888 Whole number part: 1 Fractional part: 0.732050807568877 The square root of 4 is 2 Whole number part: 2 Fractional part: 0 The square root of 5 is 2.23606797749979 Whole number part: 2 Fractional part: 0.23606797749979 The square root of 6 is 2.44948974278318 Whole number part: 2 Fractional part: 0.449489742783178 The square root of 7 is 2.64575131106459 Whole number part: 2 Fractional part: 0.645751311064591 The square root of 8 is 2.82842712474619 Whole number part: 2 Fractional part: 0.82842712474619 The square root of 9 is 3 Whole number part: 3 Fractional part: 0 The square root of 10 is 3.16227766016838 Whole number part: 3 Fractional part: 0.16227766016838
Math.Sqrt(n) - (int) Math.Sqrt(n)
C# Type Conversion and Casting
In programming, it is common to assign one type of variable to another. For example, you might want to assign an int value to a float variable, as shown here:
int i; float f; i = 10; f = i; // assign an int to a float
When compatible types are mixed in an assignment, the value of the right side is automatically converted to the type of the left side. Thus, in the preceding fragment, the value in i is converted into a float and then assigned to f. However, because of C#’s strict type-checking, not all types are compatible, and thus, not all type conversions are implicitly allowed. For example, bool and int are not compatible. Fortunately, it is still possible to obtain a conversion between incompatible types by using a cast. A cast performs an explicit type conversion. Both automatic type conversion and casting are examined here.
Automatic Conversions
When one type of data is assigned to another type of variable, an automatic type conversion will take place if
The two types are compatible.
The destination type has a range that is greater than the source type.
When these two conditions are met, a widening conversion takes place. For example, the int type is always large enough to hold all valid byte values, and both int and byte are integer types, so an automatic conversion can be applied. An automatic type conversion is also called an implicit conversion.
For widening conversions, the numeric types, including integer and floating-point types, are compatible with each other. For example, the following program is perfectly valid since long to double is a widening conversion that is automatically performed.
// Demonstrate automatic conversion from long to double. using System; class LtoD { public static void Main() { long L; double D; L = 100123285L; D = L; Console.WriteLine("L and D: " + L + " " + D); } }
Although there is an automatic conversion from long to double, there is no automatic conversion from double to long since this is not a widening conversion. Thus, the following version of the preceding program is invalid:
// *** This program will not compile. *** using System; class LtoD { public static void Main() { long L; double D; D = 100123285.0; L = D; // Illegal!!! Console.WriteLine("L and D: " + L + " " + D); } }
In addition to the restrictions just described, there are no automatic conversions between decimal and float or double, or from the numeric types to char or bool. Also, char and bool are not compatible with each other.
Casting Incompatible Types
Although the automatic type conversions are helpful, they will not fulfill all programming needs, because they apply only to widening conversions between compatible types. For all other cases, you must employ a cast. A cast is an instruction to the compiler to convert one type into another. Thus, it requests an explicit type conversion. A cast has this general form:
(target-type) expression
Here, target-type specifies the desired type to convert the specified expression to. For example, if you want the type of the expression x/y to be int, you can write
double x, y; // ... int i = (int) (x / y) ;
Here, even though x and y are of type double, the cast converts the outcome of the expression to int. The parentheses surrounding x / y are necessary. Otherwise, the cast to int would apply only to the x, and not to the outcome of the division. The cast is necessary here because there is no automatic conversion from double to int.
When a cast involves a narrowing conversion, information might be lost. For example, when casting a long into an int, information will be lost if the long’s value is greater than the range of an int, because its high-order bits are removed. When a floating-point value is cast to an integer type, the fractional component will also be lost due to truncation. For example, if the value 1.23 is assigned to an integer, the resulting value will simply be 1. The 0.23 is lost.
The following program demonstrates some type conversions that require casts. It also shows some situations in which the casts cause data to be lost.
// Demonstrate casting. using System; class CastDemo { public static void Main() { double x, y; byte b; int i; char ch; uint u; short s; long l; x = 10.0; y = 3.0; // cast an int into a double i = (int) (x / y); // cast double to int, fractional component lost Console.WriteLine("Integer outcome of x / y: " + i); Console.WriteLine(); // cast an int into a byte, no data lost i = 255; b = (byte) i; Console.WriteLine("b after assigning 255: " + b + " -- no data lost."); // cast an int into a byte, data lost i = 257; b = (byte) i; Console.WriteLine("b after assigning 257: " + b + " -- data lost."); Console.WriteLine(); // cast a uint into a short, no data lost u = 32000; s = (short) u; Console.WriteLine("s after assigning 32000: " + s + " -- no data lost."); // cast a uint into a short, data lost u = 64000; s = (short) u; Console.WriteLine("s after assigning 64000: " + s + " -- data lost."); Console.WriteLine(); // cast a long into a uint, no data lost l = 64000; u = (uint) l; Console.WriteLine("u after assigning 64000: " + u + " -- no data lost."); // cast a long into a uint, data lost l = -12; u = (uint) l; Console.WriteLine("u after assigning -12: " + u + " -- data lost."); Console.WriteLine(); // cast an int into a char b = 88; // ASCII code for X ch = (char) b; Console.WriteLine("ch after assigning 88: " + ch); } }
The output from the program is shown here:
Integer outcome of x / y: 3 b after assigning 255: 255 -- no data lost. b after assigning 257: 1 -- data lost. s after assigning 32000: 32000 -- no data lost. s after assigning 64000: -1536 -- data lost. u after assigning 64000: 64000 -- no data lost. u after assigning -12: 4294967284 -- data lost. ch after assigning 88: X
Let’s look at each assignment. The cast of (x / y) to int results in the truncation of the fractional component, and information is lost.
No loss of information occurs when b is assigned the value 255 because a byte can hold the value 255. However, when the attempt is made to assign b the value 257, information loss occurs because 257 exceeds a byte’s range. In both cases the casts are needed because there is no automatic conversion from int to byte.
When the short variable s is assigned the value 32,000 through the uint variable u, no data is lost because a short can hold the value 32,000. However, in the next assignment, u has the value 64,000, which is outside the range of a short, and data is lost. In both cases the casts are needed because there is no automatic conversion from uint to short.
Next, u is assigned the value 64,000 through the long variable l. In this case, no data is lost because 64,000 is within the range of a uint. However, when the value −12 is assigned to u, data is lost because a uint cannot hold negative numbers. In both cases the casts are needed because there is no automatic conversion from long to uint.
Finally, no information is lost, but a cast is needed when assigning a byte value to a char.
The Scope and Lifetime of Variables in C sharp
So far, all of the variables that we have been using were declared at the start of the Main( ) method. However, C# allows a local variable to be declared within any block. A block is begun with an opening curly brace and ended by a closing curly brace. A block defines a declaration space, or scope. Thus, each time you start a new block, you are creating a new scope. A scope determines what objects are visible to other parts of your program. It also determines the lifetime of those objects.
The most important scopes in C# are those defined by a class and those defined by a method. A discussion of class scope (and variables declared within it) is deferred until later in this book, when classes are described. For now, we will examine only the scopes defined by or within a method.
The scope defined by a method begins with its opening curly brace. However, if that method has parameters, they too are included within the method’s scope.
As a general rule, variables declared inside a scope are not visible (that is, accessible) to code that is defined outside that scope. Thus, when you declare a variable within a scope, you are localizing that variable and protecting it from unauthorized access and/or modification. Indeed, the scope rules provide the foundation for encapsulation.
Scopes can be nested. For example, each time you create a block of code, you are creating a new, nested scope. When this occurs, the outer scope encloses the inner scope. This means that objects declared in the outer scope will be visible to code within the inner scope. However, the reverse is not true. Objects declared within the inner scope will not be visible outside it.
To understand the effect of nested scopes, consider the following program:
// Demonstrate block scope. using System; class ScopeDemo { public static void Main() { int x; // known to all code within Main() x = 10; if(x == 10) { // start new scope int y = 20; // known only to this block // x and y both known here. Console.WriteLine("x and y: " + x + " " + y); x = y * 2; } // y = 100; // Error! y not known here // x is still known here. Console.WriteLine("x is " + x); } }
As the comments indicate, the variable x is declared at the start of Main( )’s scope and is accessible to all subsequent code within Main( ). Within the if block, y is declared. Since a block defines a scope, y is visible only to other code within its block. This is why outside of its block, the line y = 100; is commented out. If you remove the leading comment symbol, a compile-time error will occur, because y is not visible outside of its block. Within the if block, x can be used because code within a block (that is, a nested scope) has access to variables declared by an enclosing scope.
Within a block, variables can be declared at any point, but are valid only after they are declared. Thus, if you define a variable at the start of a method, it is available to all of the code within that method. Conversely, if you declare a variable at the end of a block, it is effectively useless, because no code will have access to it.
Here is another important point to remember: Variables are created when their scope is entered and destroyed when their scope is left. This means that a variable will not hold its value once it has gone out of scope. Therefore, variables declared within a method will not hold their values between calls to that method. Also, a variable declared within a block will lose its value when the block is left. Thus, the lifetime of a variable is confined to its scope.
If a variable declaration includes an initializer, then that variable will be reinitialized each time the block in which it is declared is entered. For example, consider this program:
// Demonstrate lifetime of a variable. using System; class VarInitDemo { public static void Main() { int x; for(x = 0; x < 3; x++) { int y = -1; // y is initialized each time block is entered Console.WriteLine("y is: " + y); // this always prints -1 y = 100; Console.WriteLine("y is now: " + y); } } }
The output generated by this program is shown here:
y is: -1 y is now: 100 y is: -1 y is now: 100 y is: -1 y is now: 100
As you can see, y is always reinitialized to −1 each time the body of the for loop is entered. Even though it is subsequently assigned the value 100, this value is lost.
There is one quirk to C#’s scope rules that may surprise you: Although blocks can be nested, no variable declared within an inner scope can have the same name as a variable declared by an enclosing scope. For example, the following program, which tries to declare two separate variables with the same name, will not compile:
/* This program attempts to declare a variable in an inner scope with the same name as one defined in an outer scope. *** This program will not compile. *** */ using System; class NestVar { public static void Main() { int count; for(count = 0; count < 10; count = count+1) { Console.WriteLine("This is count: " + count); int count; // illegal!!! for(count = 0; count < 2; count++) Console.WriteLine("This program is in error!"); } } }
If you come from a C/C++ background, then you know that there is no restriction on the names that you give variables declared in an inner scope. Thus, in C/C++ the declaration of count within the block of the outer for loop is completely valid. However, in C/C++, such a declaration hides the outer variable. The designers of C# felt that this name hiding could easily lead to programming errors and disallowed it.
A Closer Look at Variables in C#
Variables are declared using this form of statement:
type var-name;
where type is the data type of the variable and var-name is its name. You can declare a variable of any valid type, including the value types just described. When you create a variable, you are creating an instance of its type. Thus, the capabilities of a variable are determined by its type. For example, a variable of type bool cannot be used to store floating-point values. Furthermore, the type of a variable cannot change during its lifetime. An int variable cannot turn into a char variable, for example.
All variables in C# must be declared prior to their use. This is necessary because the compiler must know what type of data a variable contains before it can properly compile any statement that uses the variable. It also enables C# to perform strict type-checking.
C# defines several different kinds of variables. The kind that we have been using are called local variables because they are declared within a method.
Initializing a Variable
You must give a variable a value prior to using it. One way to give a variable a value is through an assignment statement, as you have already seen. Another way is by giving it an initial value when it is declared. To do this, follow the variable’s name with an equal sign and the value being assigned. The general form of initialization is shown here:
type var = value;
Here, value is the value that is given to var when var is created. The value must be compatible with the specified type.
Here are some examples:
int count = 10; // give count an initial value of 10 char ch = 'X'; // initialize ch with the letter X float f = 1.2F; // f is initialized with 1.2
When declaring two or more variables of the same type using a comma-separated list, you can give one or more of those variables an initial value. For example:
int a, b = 8, c = 19, d; // b and c have initializations
In this case, only b and c are initialized.
Dynamic Initialization
Although the preceding examples have used only constants as initializers, C# allows variables to be initialized dynamically, using any expression valid at the time the variable is declared. For example, here is a short program that computes the hypotenuse of a right triangle given the lengths of its two opposing sides:
// Demonstrate dynamic initialization. using System; class DynInit { public static void Main() { double s1 = 4.0, s2 = 5.0; // length of sides // dynamically initialize hypot double hypot = Math.Sqrt( (s1 * s1) + (s2 * s2) ); Console.Write("Hypotenuse of triangle with sides " + s1 + " by " + s2 + " is "); Console.WriteLine("{0:#.###}.", hypot); } }
Here is the output:
Hypotenuse of triangle with sides 4 by 5 is 6.403.
Here, three local variables—s1, s2, and hypot—are declared. The first two, s1 and s2, are initialized by constants. However, hypot is initialized dynamically to the length of the hypotenuse. Notice that the initialization involves calling Math.Sqrt( ). As explained, you can use any expression valid at the time of the declaration. Since a call to Math.Sqrt( ) (or any other library method) is valid at this point, it can be used in the initialization of hypot. The key point here is that the initialization expression can use any element valid at the time of the initialization, including calls to methods, other variables, or literals.
Archives
-
▼
2008
(167)
-
▼
August
(36)
- Delay Sign an Assembly in C#
- Verify That a Strong-Named Assembly Has Not Been M...
- Give an Assembly a Strong Name in C#
- Create and Manage Strong-Named Key Pairs in C#
- Access a Program Element That Has the Same Name as...
- Selectively Include Code at Build Time in C#
- Access Command-Line Arguments in C#
- Create and Use a Code Library in C#
- Create and Use a Code Module in C#
- Short-Circuit Logical Operators in C#
- Relational and Logical Operators in C#
- Arithmetic Operators in C#
- Type Conversion of Expressions in C#
- C# Type Conversion and Casting
- The Scope and Lifetime of Variables in C sharp
- A Closer Look at Variables in C#
- C# Literals
- C# Some Output Options
- C# The bool Type
- C# Characters
- C# The Decimal Type
- C# Floating-Point Types
- C# Integers
- C# Value Types
- The C# Class Library
- C# Identifiers
- The C# Keywords
- Semicolons, Positioning, and Indentation in C#
- Handling Syntax Errors in C#
- Using the Visual Studio IDE
- Using csc.exe, the C# Command-Line Compiler
- A First Simple Program
- Inheritance
- Polymorphism
- Encapsulation
- C# Object-Oriented Programming
-
▼
August
(36)