The .NET Framework and C# - C# Fundamentals - Sams Teach Yourself C# 5.0 in 24 Hours (2013)

Sams Teach Yourself C# 5.0 in 24 Hours (2013)

Part I: C# Fundamentals

Hour 1. The .NET Framework and C#


What You’ll Learn in This Hour

The .NET Framework

The C# language


Learning a new language is like learning to ride a bicycle or drive a car. You must learn the fundamentals first and build your confidence as you progress to more complex actions. When you understand the principles and have the confidence that you can accomplish your goal, suddenly that goal doesn’t seem so far out of reach. By the end of this hour, you will have a basic understanding of the .NET Framework, its components and their relationship to each other, and the C# language.

The .NET Framework

The .NET Framework provides developers with the tools and technology to create and run next-generation applications and web services in a way that is language and platform independent. It has a rich class library that supports many common tasks and simplifies many difficult tasks, enabling you to focus your time more effectively on the problem at hand: solving the business needs in the most efficient manner possible. Code written for the .NET Framework is called managed code, whereas any other code is called unmanaged code.

Just as code written for the .NET Framework is called managed code, the resulting application is called a managed application. When a managed application runs, it automatically hosts the common language runtime it was built against. Not only does the .NET Framework provide a number of different runtime hosts, it also provides the tools necessary to write your own. Because of this capability, unmanaged applications such as Internet Information Services (IIS) and Microsoft SQL Server can host their own copy of the common language runtime, enabling them to take advantage of both managed and unmanaged features.

The .NET Framework is designed to do the following:

• Provide a runtime environment that simplifies software deployment and reduces the chances of version conflicts.

• Enable the safe execution of code.

• Use industry standards for all communication to enable integration with non-.NET code.

• Provide a consistent developer experience across all types of applications in a way that is language and platform independent.

• Provide a runtime environment that minimizes or eliminates the performance problems of scripted or interpreted languages.

To achieve these goals, the .NET Framework has four components.

The Common Language Runtime

The common language runtime (CLR) is the core of the .NET Framework and provides a unified type system and a managed runtime environment. Just as your heart provides the pumping action your body needs to function, the common language runtime provides the low-level core services your application needs to function and is said to manage your code. Together they form a foundation for developing and executing applications that are language and platform independent and help eliminate, or at least reduce, many common programming errors.

Common Type System

The unified type system, called the common type system (CTS), enables all .NET languages to share the same type definitions, enabling those types to be manipulated in a consistent manner. This helps ensure correctly written applications by

• Removing the possibility that incompatible data can be assigned to a type

• Enabling every .NET language to have the same description of a type, regardless of what language was used to define that type

• Enforcing a consistent manner in which a language manipulates a type


Go To

We discuss types a bit later in this hour. HOUR 3, “UNDERSTANDING C# TYPES,” provides more detailed information.


Because the common type system specifies the definition of how types look and behave in a language-independent fashion, it must take into account differences in those languages. The common type system provides a minimum set of rules a .NET language (and, consequently, its compiler) must follow, called the common language specification (CLS). This common definition also enables the idea of language integration, which enables you to use a type defined in another language as if it were defined natively in your language.


Note: Type Safety and the CTS

The common type system and common language specification form the foundation of the type safety found in the .NET Framework.

This foundation provides the .NET Framework a consistent way to promote type safety but not enforce it. The task of enforcing type safety is left to the individual language compilers and the virtual execution system (which you learn about a bit later in this hour).


Common Intermediate Language

The common type system and common language specification help meet the goal of being language and platform independent, but it does no good if the compiler generates executable object code tied to the hardware platform. To resolve this problem, managed code is partially compiled into a low-level language called common intermediate language (CIL). You can think of common intermediate language like assembly language; it is made up of individual, low-level instructions that represent your code.

An assembly is a partially compiled unit, or package, that contains CIL instructions and provides a logical boundary for defining types. Because assemblies are partially compiled, they can be either 32- or 64-bit, depending on the operating system and hardware. This capability truly means that managed applications are platform independent and, at the same time, can take advantage of hardware technology without recompiling or adding special instructions to your code.


Tip: CLS Compliance

Almost all the classes provided by the framework class library are CLS compliant, so any .NET language will have access to the same library. If you are developing your own library, it is suggested that you also ensure that your classes are CLS compliant to allow for the widest adoption and use possible.


Virtual Execution System

The other important part of the common language runtime is the managed runtime environment, called the virtual execution system (VES), which handles the low-level core services your application needs. Just as Java applications require the Java virtual machine (JVM) to run, a managed application requires the CLR and, more specifically, the VES to run.

When a .NET application starts, it is the VES that is responsible for actually loading the CIL code, executing that code, and, ultimately, managing the memory allocations required by the application. In other words, the VES provides the services and infrastructure to abstract both platform and language differences.

As part of the loading and compilation process, the VES performs various validation and verification checks to ensure that the file format, assembly metadata, and CIL are consistent and that the CIL instructions themselves do not allow illegal memory access. This ensures that an application can access only memory or other resources to which it has been explicitly granted access. This restricted environment can be thought of as a sandbox.

If the VES provides a runtime environment and executes assemblies containing CIL, are those assemblies interpreted or compiled? Remember, one of the goals for the .NET Framework is to provide a runtime environment that minimizes or eliminates the performance problems of scripted or interpreted languages. This would imply that the CIL code is compiled, but when does that compilation happen?

One of the services the VES provides is the Just-In-Time (JIT) compiler. Just-In-Time compilation is the process of taking the partially compiled CIL code and generating executable object code, or native code, at runtime.


Tip: Just-In-Time Compilation

The process of Just-In-Time compilation is called jitting and the JIT compiler is also called the jitter.


By compiling the code in this manner, the .NET Framework gains a considerable speed improvement over traditional interpreted languages. Just-In-Time compilation also has benefits over regular (static) compilation, as it can enforce security guarantees at runtime and recompile the code at runtime to gain additional optimizations. The .NET Framework JIT compiler is highly optimized for compiling CIL code into highly efficient object code, runs on demand, and caches the compiled code for future use.

Memory Management and Garbage Collection

Proper memory management is a classic problem in many unmanaged programming languages and is a potential source for some common errors. In these languages, the developer is responsible for allocating and deallocating memory at the correct times. The .NET Framework resolves this problem by controlling these memory allocations and deallocations automatically as part of the VES.

It is this automatic memory management, also known as garbage collection, which makes C# (and the other .NET languages) a garbage-collected language. Garbage collection frees you from having to worry as much about releasing memory when it is no longer needed. This enables you to create applications that are more stable by preventing many of those common programming errors and focusing your time on the business logic your application requires.

Even with automatic memory management, it is still important to understand how the garbage collector interacts with your program and the types you create. An in-depth discussion on garbage collection is well outside the scope of this book, but we talk a little bit more about it in Hour 23, “Memory Organization and Garbage Collection.”

Framework Class Library

Although the CLR forms the core of the .NET Framework, the framework class library (FCL) actually gives it substance. The class library is similar to Java’s class libraries, the C++ Standard Template Library (STL), Microsoft’s Active Template Library (ATL), the Microsoft Foundation Classes (MFC), Borland’s Object Windows Library (OWL), or any of the various other class libraries available today.

Just like those class libraries, the FCL is a rich collection of reusable classes, or types, enabling you to achieve a high level of developer productivity by simplifying many common programming tasks.


Note: Framework Class Library

The framework class library contains more than 4,000 public classes and is one of the largest class libraries available today. It is the best example in the .NET Framework of making the simple things easy and the hard things possible.

Although it is possible to create an application without using the types provided by the FCL, it is impractical to do so.


Figure 1.1 shows some of the types available in the FCL, grouped by functional area.

Image

Figure 1.1. Framework class library.

At the lowest level are the base class libraries (BCL) that serve as the standard runtime for any .NET language and provide types that represent the intrinsic CLR types, collections, streams, string manipulation, basic file access, and a variety of other operations or data structures. The remaining classes in the FCL are focused on specific functional areas, such as providing data access, extensible markup language (XML) support, globalization support, diagnostics, configuration, networking, communication, business workflow support, web applications, and Windows desktop applications, to name just a few.

Namespaces

With thousands of classes in the .NET Framework class library, there needs to be a way to prevent ambiguity between type names and to provide a convenient hierarchical grouping mechanism. The .NET Framework uses the concept of namespaces to accomplish this. A namespace is simply a collection of types and has no effect on the accessibility of a type. Namespaces can be split across multiple assemblies. The .NET Framework uses the hierarchical nature of namespaces to provide a progressive framework, creating a powerful and easy-to-use development platform.


Note: Namespaces and Type Names

Namespaces use a dotted syntax to denote a hierarchical grouping, with each level in the hierarchy separated by a dot (.).

Given a type’s full name, everything up to the rightmost dot is the namespace, whereas the last part (after the rightmost dot) is the type name. For example, System.Printing.PrintDriver is the full name for the PrintDriver type in the System.Printing namespace.

Namespaces, however, are only conveniences supported by the .NET programming languages. In the CLR, a type is always identified by its full name, which contains both the name of the type and its containing namespace.


Almost 400 namespaces exist in the .NET Framework class library, although you will probably never interact with some of them. As you become more familiar with the class library, you will find certain namespaces that you use more frequently than others, which might be a different set than ones your co-workers or peers use.

Some of the commonly used namespaces are shown in Table 1.1.

Table 1.1. Commonly Used Namespaces

Image

Image

Parallel Computing Platform

Writing multithreaded and asynchronous applications has always been possible in both managed and unmanaged code; however, it has also been difficult to get correct. The .NET Framework simplifies writing these applications with the parallel computing platform, which is a programming model for both managed and unmanaged code that raises the level of abstraction so you no longer need to worry as much about the lower-level concepts, such as threads and locks.

For managed code, the parallel computing platform includes parallel implementations of the common loop instructions, a parallel implementation of LINQ to Objects, and new lock-free and thread-safe collections. Visual Studio also includes diagnostic tools, such as the parallel concurrency analyzer and processor migration analysis that enable you to easily debug and tune your code.

The parallel computing platform simplifies the mechanics of writing code that can effectively take advantage of multiple processors. The decision of what code is right for parallelism still requires analysis and, ultimately, changing the way you think about how to solve a particular problem. We touch on some of these aspects of the parallel computing platform in Hour 24, “Understanding Threads, Concurrency, and Parallelism.”


Go To

HOUR 24, “UNDERSTANDING THREADS, CONCURRENCY, AND PARALLELISM,” for more information on multithreaded and parallel programming.


Dynamic Language Runtime

The dynamic language runtime (DLR) is an additional runtime environment providing language services and support for dynamic languages. Being built on top of the common language runtime means these dynamic languages can now integrate with other .NET languages. The DLR also enables dynamic features for existing statically typed languages such as C#, enabling them to support consistent expressions when working with dynamic objects from any source.

With the inclusion of the DLR, the support for dynamic languages, and enabling dynamic features in static languages, developers are now free to choose the best language possible to solve the task and be certain that other developers and other .NET languages can easily use the dynamic code they create.


Go To

HOUR 22, “DYNAMIC TYPES AND LANGUAGE INTEROPERABILITY,” for more detailed coverage on integrating with dynamic languages.



Tip: What Is a Dynamic Language?

In a language such as C#, which is statically typed, the compiler attempts to prove type safety, and, if it cannot, generates an error. In a dynamic language, this attempt at proving type safety is not made. In addition, most dynamic languages perform more complex type operations, such as determining the correct method overload, at runtime, whereas C# performs this type of resolution at compile time.

In effect, what would normally be done at compile time in a statically typed language is done at runtime. This includes the idea that you can generate code at runtime (using what is commonly called an eval or repl loop) that can modify the state of running objects. As a result, dynamic languages enable a great deal of freedom and are most frequently used as scripting languages.

Some common dynamic languages are JScript, JavaScript, Python, and Ruby.


The C# Language

If you are a C, C++, or Java programmer, C# will be immediately familiar because it shares a similar syntax. If you are already familiar with Visual Basic (any version of Visual Basic that runs on the .NET Framework, not Visual Basic 6.0 or earlier), the syntax might seem foreign but the framework class library will be familiar. For those of you who have never worked in any of these languages, you will soon find that developing with C# is easier than many other languages due to the elegant syntax and rich class library.


Tip: Language Inspiration

As a language, C# has drawn inspiration for its syntax and primary features from a number of different languages, including Delphi 5, C++, and Java 2.

The generic type system (which you learn more about in Hour 12, “Understanding Generics”) drew from the generic type systems in Eiffel and Ada. Haskell and Lisp were the primary inspirations for query comprehensions in LINQ and lambda expression evaluation (seeHour 13, “Understanding Query Expressions”).

C# also added features found in dynamic languages such as Ruby and functional languages like F#.


Like many modern programming languages, C# is an object-oriented language and fully supports the object-oriented programming concepts of inheritance, polymorphism, encapsulation, and abstraction. In addition to being an object-oriented language, C# also supports component-oriented programming, which enables you to specify units of functionality (components) that are self-contained and self-documenting by presenting a model with properties, methods, events, and metadata about the component. C# has support for these concepts directly in the language, making it a natural process to create and use components.


Go To

HOUR 4, “UNDERSTANDING CLASSES AND OBJECTS THE C# WAY,” for more information on object- and component-oriented programming.


C# has language features enabling developers to take advantage of the advances and improvements made in the CLR. Garbage collection automatically manages memory. Exception handling creates a structured and extensible way to detect and recover from errors. As a type-safe language, it is impossible to have uninitialized variables, illegally access memory, or store data of one type in a location that can accept only a different type.


Go To

HOUR 11, “HANDLING ERRORS USING EXCEPTIONS,” for more information on exception handling.


In addition, C# also has language features and syntax designed to reduce the amount of boilerplate code you must write, making your code less complex and reducing the chance for making common errors. In some cases, these are nothing more than simple changes in syntax, simplifying complex or error-prone language features, and are readily accessible and easily understood; in other cases, these improvements enable scenarios that are more advanced.

C# continues to evolve with each new release, adding new language features and syntax, always striving to achieve the goal of making the simple things easy, the difficult things possible, and the bad things difficult. As C# adds new capabilities, the simple things become easier, the difficult things become easy, and the things not previously possible become possible.

Types

In C#, types describe values. Any time you want to use a value, you need a type. As you saw when you learned about the common type system, a type defines the allowed values and operations supported by those values. Every value in C# is fully described by its exact type and is an instanceof that exact type. Being fully described means that the type unambiguously defines both the representation of and operations on a value.

Types in C# are divided into value types and reference types. Value types describe values that are completely self-contained and include numeric types, enumerated types, and structures. Reference types, however, store a reference to a value rather than the actual value.


Go To

HOUR 3, “UNDERSTANDING C# TYPES,” for a more in-depth look at the difference between value and reference types.


C# provides many predefined value types and a few predefined reference types. It also enables you to create your own user-defined types. In upcoming hours, you explore, in more detail, the difference between value types and reference types and how to create your own. For now, however, the most important difference is that a value type is copied “by value” because it contains the actual value, whereas a reference type contains a reference to the actual data.

Statements and Expressions

A statement is simply a single, complete program instruction that must end with a semicolon (;). Only specifying a single instruction seems like it would be restrictive, but C# also gives us the idea of a statement block, which is simply a group of statements enclosed by “curly” braces ({ and }). You can use a statement block anywhere you would normally use a single statement.

Because statements end with a semicolon, you are free to use whitespace (such as a space character, tab character, or newline) in a way that helps visually orient your code. The best approach is to adopt a simple and consistent style (if your company or team does not already have one) to make your code easier to read and maintain.


Caution: Whitespace

Even though the compiler generally ignores whitespace, the whitespace between a type declaration, its identifier, and any other keywords is important. Without whitespace here, the compiler can’t distinguish the keywords.


An expression evaluates to a value. If you consider a statement to be a program action, an expression is a computation. Expressions that result in a Boolean value (either true or false) are most commonly used to test if one or more conditions are true and are called Boolean expressions.

Variables and Constants

The simplest definition for a variable is that it represents a storage location whose value can change over time. The most common forms of variables are local variables and fields, both of which are defined by providing a type, an identifier, and, optionally, an initial value:

int a;
int b = 1;

If you are declaring multiple variables of the same type, you can combine the declarations, as follows:

int a, b;

When a variable is declared inside of a limited scope (such as a method), it is said to be a local variable and is accessible by name only from within that scope.


Note: Scope, Declaration Space, and Lifetime

Scope can be thought of as a container in which it is legal to refer to a variable by its unqualified name. This is different from the declaration space, in which no two identifiers are allowed to have the same name. If scope defines where you can use a name, declaration space answers where that name is unique.

The lifetime of a variable is closely connected to its scope and defines how long the variable will be accessible. A variable is guaranteed to be alive at least as long as its scope is executing.

You learn about scope and declaration space in more detail in Hour 4.


A field is simply a variable that is not declared inside of a limited scope and can be associated with either the type itself, in which case it is a static variable (which you can think of as the equivalent to a global variable), or with an instance of the type, in which case it is an instance variable. Local variables and fields must be initialized before they are used and are accessible only while the block containing their declaration is executing.

The code in Listing 1.1 shows a Color type that has private instance fields named red, blue, and green and public static fields named White, Red, Blue, and Green.

Listing 1.1. A Color Class


class Color
{
private byte red;
private byte blue;
private byte green;

public Color(byte red, byte blue, byte green)
{
this.red = red;
this.blue = blue;
this.green = green;
}

public static Color White = new Color(0xFF, 0xFF, 0xFF);
public static Color Red = new Color(0xFF, 0, 0);
public static Color Blue = new Color(0, 0xFF, 0);
public static Color Green = new Color(0, 0, 0xFF);
}


The static fields are initialized at some point before they are used, but afterward, there is nothing to prevent them from being changed. To accommodate the idea of declaring a field that cannot be changed after it has been assigned, C# enables you to create read-only fields.

Listing 1.2 shows the changed lines of the Color class.

Listing 1.2. A Color Class Using Read-Only Fields


class Color
{
// ...

public static readonly Color White = new Color(0xFF, 0xFF, 0xFF);
public static readonly Color Red = new Color(0xFF, 0, 0);
public static readonly Color Blue = new Color(0, 0xFF, 0);
public static readonly Color Green = new Color(0, 0, 0xFF);
}



Go To

HOUR 4, “UNDERSTANDING CLASSES AND OBJECTS THE C# WAY,” for more information on read-only fields.



Tip: Literal Values and “Magic Numbers”

Literal values are generally numeric values that have special fixed meanings specified directly in code. Over time, the meaning of these literal values can be lost, making that part of the code difficult to maintain. As a result, these literals are often called “magic numbers.” By using constants instead of literal values, the meaning is preserved, making the code self-documenting.

How long would it take you to figure out what the number means in the following function?

static float Compute(float f1)
{
return 299792458 / f1;
}

Now, if that same function were written using a constant, the meaning of that “magic number” becomes clear:

static float Compute(float f1)
{
const float SpeedOfLight = 299792458;

return SpeedOfLight / f1;
}

In our example, the value 299792458 is a literal value and would therefore be considered a magic number. As you might have guessed, constants are preferred over using just literal values because they have names that can provide more meaning than just a number, and you can guarantee that its value has not changed.


A constant represents a value that can be computed at compile time. Constants are associated with the type itself, as if they were static. Like variables, constants can be declared inside of a limited scope or globally. Unlike variables, a constant must always be initialized when it is declared.

A statement that declares a variable or a constant is generally called a declaration statement and can appear anywhere within a block.

Identifiers and Keywords

When you declare a variable, field, or constant, you must provide both the data type and a meaningful name called an identifier.

Identifiers must follow these rules:

• Only letters (uppercase and lowercase), digits, and the underscore character are valid.

• An identifier must begin with a letter or the underscore character, although using an underscore (or multiple underscores) as the beginning character for any publicly accessible identifier is considered poor style and should be avoided.

• Identifiers must be unique within a given declaration space.

• Identifiers are case sensitive.

You should follow these additional guidelines when choosing identifiers:

• Identifiers should be easily readable.

• Identifiers should not use abbreviations or contractions as part of the name.

• Identifiers should convey the meaning or intent as much as possible.

In C#, identifiers are case sensitive. The recommended naming conventions suggest using camel casing notation, which capitalizes the first letter of each word except the first word (for example, bookTitle) for variable and parameter names and Pascal casing notation, which capitalizes the first letter of each word (for example, BookTitle) for methods and other identifiers.


Tip: Camel and Pascal Casing

Camel casing is so named because the sequence of letters looks like the humps on a camel’s back. Pascal casing was named after the style popularized by the Pascal programming language (and because Anders was the original designer of the Turbo Pascal language).

Microsoft no longer recommends using Hungarian notation or using the underscore character to separate words, both common in other languages.


If you are already familiar with another case-sensitive language, such as C, C++, or Java, this should feel normal to you. However, if you are coming from a language that is not case sensitive, such as Visual Basic, this might take a bit of practice. Fortunately, the Visual Studio 2012 code editor has features that can help make that transition easier.

Because identifiers define the names of specific elements, it is reasonable that the C# language also needs to use identifiers to indicate special meaning to the compiler (and to you). As a result, there are certain identifiers, called keywords, which have been reserved for use by the language itself.

There are 77 keywords in C# that are reserved at all times; these are listed in Table 1.2.

Table 1.2. C# Keywords

Image

An additional 26 keywords, known as contextual keywords, have special meaning only in limited circumstances, or context. Outside of that context, these keywords can be for your own purposes, although to minimize confusion, you should try to avoid doing so, if possible. The contextual keywords are listed in Table 1.3.

Table 1.3. C# Contextual Keywords

Image

Summary

At the beginning of the hour, you looked at the .NET Framework and the components that are part of it. This might have been a little more in-depth than what you were expecting for the first hour, but having at least a basic understanding of why the .NET Framework was created and how it is put together is essential to becoming a well-rounded and successful .NET programmer. From those beginnings, you learned about the C# language and were introduced to statements, expressions, variables, constants, identifiers, and keywords.

Throughout the rest of this book, each hour builds upon what you learn from the previous hours and progresses from learning the fundamentals of C# and how it provides support for both object-oriented and component-oriented programming, all the way to learning about more advanced topics such as multithreading and parallel programming. Along the way, you build a more complete real-world application, from “soup to nuts” as the saying goes, and you build a solid foundation in C# and .NET programming on which you can build larger and more complex applications.

Q&A

Q. What is the .NET Framework?

A. The .NET Framework is a platform enabling developers to create and run next-generation applications and web services in a way that is language and platform independent and helps eliminate, or at least reduce, many common programming errors.

Q. What is the common language runtime (CLR)?

A. The common language runtime (CLR) is the core of the .NET Framework upon which C# runs.

Q. What is the difference between a managed application and an unmanaged application?

A. Code written for the .NET Framework is called managed code, whereas any other code is called unmanaged code.

Q. What is meant by garbage collection and why is it important?

A. Garbage collection is a runtime service provided by the .NET Framework that frees you from having to handle memory allocation and deallocation manually. This enables you to create more stable applications by preventing many of those common programming errors and enables you to focus your time on the business logic your application requires.

Q. What is C#?

A. C# is an object-oriented, type-safe programming language that runs on the .NET Framework.

Q. Are C# programs compiled?

A. Yes, C# programs are compiled at development time to common intermediate language (CIL). At runtime, they are compiled to executable object code by the Just-In-Time (JIT) compiler.

Workshop

Quiz

1. What are the components of the .NET Framework?

2. Why is the common type system important?

3. What is common intermediate language (CIL)?

4. Why is the framework class library important?

5. What does the dynamic language runtime (DLR) provide to C#?

6. Is the following code valid in C#?

class Program
{
static void Main()
{
const int LettersInEnglishAlphabet = 26

system.console.WriteLine(
"There are {0} letters in the English alphabet.",
LettersInEnglishAlphabet)
}
}

7. What is the correct interpretation of the following variable declarations?

int a, b = 1;

8. Which of the following is not a valid identifier?

A. lightHouse

B. _lighthouse

C. 22lighthouse

D. lighthouse2

Answers

1. The .NET Framework has four major components: the common language runtime, framework class library, parallel computing platform, and dynamic language runtime.

2. The common type system is important because it gives every .NET language the same description of a type and defines how that type can be used, which enables language integration.

3. Common intermediate language is the low-level language into which managed code is partially compiled. You can think of common intermediate language like assembly language; it is made up of individual, low-level instructions that represent your code.

4. The framework class library provides a rich set of reusable types available to all .NET languages and enables you to achieve a high level of developer productivity by simplifying many common programming tasks.

5. The DLR enables C# to work with dynamic objects from any source (COM, IronRuby, IronPython, and JavaScript, to name a few) using a consistent syntax.

6. No, the code shown is not valid C# for two reasons. First, none of the statements end in a semicolon (;). Second, the correct type name is System.Console.WriteLine not system.console.WriteLine because C# is a case-sensitive language.

7. Combining multiple variable declarations and initial value assignments like this is dangerous because it can be ambiguous. The correct interpretation of this statement is equivalent to the following:

int a;
int b = 1;

8. The correct answer is C. Identifiers cannot start with a number.

Exercises

There are no exercises for this hour.