This article has been excerpted from book "Visual C# Programmer's Guide"
C#'s design started on a blank page, taking into account the current state of programming languages. The designers drew on the strengths of existing languages and avoided their flaws. What emerged is a full-featured, object-oriented language of rare beauty and grace. Based on C, C++, and, most would say, Java, C# is a strongly typed language that C++ and Java developers will find very familiar. It is easy to learn and has none of the backward-compatibility issues of C or C++. Created for the .NET environment, C# has a thoroughly modern syntax with accessors (properties), a concept borrowed from COM, and new operators, such as implicit and explicit. While affording easy access to the framework's class library, it also allows direct memory and pointer access with the unsafe keyword. There is also talk of extending language support to templates, or parameterized types.
Among the stated design goals of the C# inventors, Anders Hejlsberg, Scott Wiltamuth, and Peter Golde, was the creation of a modern, simple, general-purpose programming language. They also intended to craft a language flexible enough to handle the contradictory demands of hosted and 10 C# Corner embedded systems running on distributed networks. As you become familiar with C#, you might agree that the inventors have more than succeeded.
You should be aware of an important limitation of C#: When exceptional or time-critical performance is required, it is probably wise to retreat to C or C++. In the C# specifications submitted to the ECMA, the section outlining C#'s design goals clearly states that C# is not intended to compete directly with C or assembly language on performance or executable file size.
Managed code is simply code written to execute in the .NET environment. Managed code offers a few benefits in terms of integrated security, type-safe code, and automatic memory allocation and deallocation.
In the .NET world, there is no delete keyword. No longer are hours, if not days, spent tracking down a stubborn memory leak. Memory allocation and deallocation is done via the common language runtime (CLR). This garbage collection is handled by the System.GC, which periodically walks the heap to clean up obsolete objects. Although you are not required to dereference objects with null, it is still good programming practice to do so; and it delivers added benefit in that the Garbage Collector (GC) is given early notice that the object is not needed.
Security is integrated into .NET from the ground up. With .NET comes support for role-based security and code access security. In traditional role-based security, authentication (the verification of a person or process) and authorization (a means of determining that a person or process is permitted to perform a requested process) are carried out by any one of many commonly used mechanisms, such as .NET Passport, NTLM, or an application-defined mechanism. Code access security takes a different approach. The idea behind this .NET innovation is that code itself can be malicious, as any victim of a virus or worm will attest. Code access security allows code behavior to be restricted by varying degrees, depending on the code's origin and identity.
Currently, most application boundaries are normally confined to the process level. Any communication outside of the process requires specialized handling such as a COM component. Application domains, a concept introduced in .NET, permit multiple applications to run in a single process. Before managed code runs, it goes through a verification procedure to establish that it will not access memory or perform an action that would cause the process to crash. If the code passes this verification, it is said to be type safe. Type-safe code allows the CLR to define application domains, which carry the same level of isolation and protection as a process. While not totally free of charge, interapplication communication, across application domains, avoids most of the performance penalties associated with interprocess communication.
The hurdle of calling methods across different languages is almost as high as the barrier to interaction between different operating systems. TCP/IP aided in communication between operating systems, just as COM components went a long way to facilitate cross-language conversations. But there were constraints. Visual Basic only has signed integer and long data types, while Unicode characters are not native to C++. In many instances, performance would suffer as data was converted or cast back and forth. In .NET, all compliant languages are equal; and, with Microsoft Intermediate Language (MSIL), all operating systems are potentially equal as well. Efforts toward platform independence are taking shape with the Mono Project, a .NET implementation on Linux. A .NET-compliant language is one that adheres to the Common Language Specification (CLS) and the Common Type System (CTS) laid out in the CLI. It is now possible to have C#, managed C++, and VB.NET code in the same module. Other compliant languages include J# (Java Sharp), Cobol, .NET and the .NET Framework 11 and Perl. MSIL has made programming languages transparent. Systems can be developed in the language or languages of choice.
Of course, becoming a compliant language might force some concessions to be made. Visual Basic appears to have mutated to such an extent that VB.NET could qualify as a new .NET language along with C#. C++ is confined to single inheritance. In all likelihood, other languages that adhere to the CLS will have minor restrictions placed upon them.
This newfound freedom may come at a cost. It brings to mind an old adage in the C world: C gives the developer plenty of freedom-enough to hang himself! In the same way, developing an application in multiple .NET languages sows the seeds for project failure. An application written using C#, VB.NET, J#, Cobol, and Perl requires an army of skill sets, rather than a cohesive team, to maintain the code. C# will likely be the language of choice for most projects, because of current and future features such as the unsafe keyword and possible support for templates. Other serious contenders are probably VB.NET and J#.
The Class Library
What can be said about the framework's class library? It is extensive and extensible. There is finally a consistent method to access system resources spanning the many .NET-compliant languages. Certainly, at first glance, the number of namespaces, classes, and interfaces can be daunting, and here seasoned C++ and Java developers will hit a learning curve. Developers migrating to the .NET Framework from C++ or Java should have an easier transition because they have already grasped object-oriented concepts. Those coming from a procedural language world, such as Visual Basic and Cobol, face a much sharper bend on the road to mastering the .NET Framework.
The class library is broken down into a hierarchy of namespaces. Each namespace's name describes its general purpose. Contained in each namespace are the classes and interfaces, which define specific behaviors and data that contribute to the namespace's functionality. The class library is not language specific and may used by any of .NET's compliant languages.
In conjunction with the CLR, the class library supports not only dynamic runtime binding but also dynamic compile-time binding. Although the term compile-time binding is open to debate, the practice isn't. If an object is not present, it is possible for an application to obtain the source code and compile it on the fly. Calling the compiler, in the System.CodeDom.Compiler namespace, with either a string or a file name returns a reference to the executable code. The results can then be loaded and the object's methods executed, thus giving a whole new meaning to late binding.
The class library is a comprehensive implementation that addresses 80 percent of problem domain needs with an out-of-the-box answer. But, as is always the case, a customized solution is the only alternative in some situations. In designing the class library, the developers paid a great deal of attention to extensibility. The library features a rich set of virtual and abstract classes and methods, as well as a wide range of interfaces to tackle any customization requirement.
Windows application distribution has always been fraught with potential disaster. COM executables and DLLs have to be registered. Likely, every developer has had nightmares involving the Windows registry. Did I register everything? Are all the proxy/stubs accounted for? And that's only for local machine registry-in DCOM the proxy/stub has to be registered on both client and server machines. And if you're using a customized surrogate, you had better start writing an installation application. Microsoft describes this situation as "DLL Hell."
Updating shared DLLs is also a form of DLL Hell and commonly creates havoc. The newer DLL versions overwrite the current ones, or registration conflicts occur. The newer versions are backward-compatible with only some of the existing software. Consequently, some applications run normally while others inexplicably crash. Tracking down which DLL is the culprit and where the bug is can be a long, agonizing task. .NET eliminates the clash of old and new with assemblies.
An assembly, the name .NET uses for an executable or DLL, contains a manifest and metadata in addition to the compiled source code. Together, the manifest and metadata fully describe the contents of the assembly. Each assembly is a self-contained unit with compiled source code, a full description of the code and its dependencies, a version number, and security settings. Assemblies can be private or shared. In most cases, to install private assemblies, it is a simple matter of using XCOPY. To uninstall, use DEL. Shared assemblies require a bit more work and are discussed in detail in Chapter 26. Shared assemblies are installed in the global assembly cache (GAC).
Common Language Infrastructure
In previous sections, terms such as CLI and CTS have been bandied about without any explanation. Beginning with this section, we'll start to explore what makes .NET run. The CLI can be broken down into three main components:
Common Type System
The CTS lays out the basic data types in order for compiler designers to build a .NET-compliant language.
Common Language Specification
The CLS establishes the minimum set of rules to promote language interoperability. In conjunction with the CTS, it describes the contract all compilers must meet when targeting .NET.
Virtual Execution System
The VES is responsible for loading and running programs and providing services for those programs.
These three elements, and the use of metadata by the VES, constitute the CLI. Without putting too fine a point on it, the CLI can almost be thought of as specifications for a virtual operating system or machine. In the Windows world, the CLI comes to life in the CLR.
Aside from the three main components, a fourth component in the CLI is metadata, or data about data. From a CLI perspective, metadata is information about every element in an assembly. The importance of metadata cannot be overstated. One feature of .NET that is well known to Java developers but unknown in the C++ world is reflection, the ability to interrogate assemblies and objects at runtime. Reflection enables dynamic creation and invocation.
Enough metadata is in an assembly that the CLR can control all aspects of execution, including loading, execution, and inspecting the execution state (i.e., walking the stack); enforcing security; and establishing runtime context boundaries (i.e., where one application domain begins and another ends).
Common Type System
The CTS is one of the cornerstones of .NET. This type system permits .NET to support multiple languages-a reasonable expectation, considering that languages have great difficulty interoperating if they are based on dissimilar types. This multilanguage support must take into account both procedural and object-oriented languages. How is this accomplished? The quick answer is that all types are objects.
Although the CTS is a hierarchy of types, as shown in Figure 2.1, it can be distilled down to two root types: value and reference. The single most distinguishing feature between these basic types is memory allocation. Value types are allocated on the stack, while reference types are allocated on the heap.
Figure 1: The CTS Type Hierarchy
Although accurate, the earlier statement that all types are objects is somewhat misleading. For each value type, the CTS supports a corresponding reference type. Therefore 2.ToString() is a valid statement.
The built-in value types consist of primitive types such as integers and longs. User-defined value types are types such as structures and enumerations. An object reference type can be said to be a class. An interface reference type has no implementation but simply defines method and property signatures that must be implemented by an object. A pointer reference type is a value that identifies a memory location in the heap.
Common Language Specification
The CLS defines the minimum requirements that must be realized before a language is considered .NET compliant. It is primarily of use to language and framework designers. It specifies a minimum subset of CTS types to be supported and a set of usage rules. From an application developer's perspective, where language interoperability is crucial, Table 2 lists the types supported by the CLS. This is likely to be the only instance where the CLS need be consulted.
Figure 2: The CTS type hierarchy
Supported CLS types are only a small subset of defined CTS types. Keep this in mind if language interoperability is a requirement. C# supports unsigned and signed values, whereas VB.NET supports only signed types. Therefore, to be CLS compliant, a C# component should expose only signed types and use unsigned types only with an accessibility level of private or internal or as locally scoped variables. (This rule is opposite for int8 or Byte, whose unsigned value is CLS compliant and whose signed value is not).
Common Intermediate Language
The Common Intermediate Language (CIL), more commonly known as Microsoft Intermediate Language (MSIL) or simply Intermediate Language (IL), is a CPU-independent instruction set that allows .NET to support multiple languages. It also has the potential to be operating system- independent as long as the platform can host an implementation of the CLI. The major difference between IL and Java's byte code is that IL is only an intermediate step on the path to execution. The Java Virtual Machine runs an application by interpreting its byte codes. .NET compilers, on the other hand, produce an assembly that is loaded by the CLR at runtime.
Common Language Runtime
The CLR provides the environment in which managed code is executed. In addition to implementing the CLI, the CLR adds Windows-specific functionality, such as security, GUI capabilities for the Win32 environment, and COM interoperability. Again, without stretching the point too far, the CLR may be perceived as a virtual machine but, in fact, it isn't. The CLR reads an assembly and compiles it to the native instruction set of the processor, via a Just-In-Time (JIT) compiler, prior to execution.
Figure 3 shows the sequence of steps taken to compile an assembly.
Figure 3: The C# Compiling Sequence
Before an application can run in the .NET environment, a runtime host, which is a stub of unmanaged code, must load and initialize the CLR. An example of such a host is aspnet_isapi.dll, an Internet Server API extension DLL that loads the CLR and creates the application domain for an application's subsequent ASP.NET Web requests. Various runtime hosts are currently distributed with the .NET Framework, including one for Microsoft Internet Explorer and one for all shell executable applications. It is likely that future versions of operating systems that support the CLR will have any required runtime hosts built in.
The sequence of loading and executing an application is graphically illustrated in Figure 4.
Figure 4: Running an Application
Once the CLR has been loaded and initialized and an application domain created, the application is ready to run. At this point, the CLR loads the application in the domain and then verifies that the IL compiled code is type safe. If verification fails, an exception is thrown. Verification can be skipped if a security policy (see Chapter 22 for a detailed explanation) is in place to do so. Once verification succeeds or is skipped, the JIT compiler compiles the IL to native code. 16 C# Corner Not all the code in an assembly is verified and compiled at the outset. The JIT converts the IL to native code as an object's methods are called during program execution. The resulting native code is cached and used for subsequent calls of the method. It does this by marking the method, which in turn points subsequent calls to the binary code.
Hope this article would have helped you in understanding benefits of the .NET Framework. See my other articles on the website on .NET and C#.