Chapter 2. Why Use C#? Why Use .NET?

 

As soon as we started programming, we found out to our surprise that it wasn’t as easy to get programs right as we had thought. Debugging had to be discovered. I can remember the exact instant when I realized that a large part of my life from then on was going to be spent in finding mistakes in my own programs.

 
 --Maurice Wilkes

Ever since the introduction of computers, there has been exponential growth in businesses embracing technology to solve their corporate problems. Computers have evolved and matured enough to support massively distributed and heterogeneous applications in both desktop and Internet environments. As the technology becomes more complex, so do the problems that developers have to solve in order to produce a good product. While there are many technologies and development tools available, there are also numerous issues that inhibit productivity or development.

There is the ongoing controversy surrounding the right programming language and platform for the job. Many times, certain features are only available with certain programming languages, such as automatic memory management, which often ends up dictating the language to use for the job. In a perfect world, the language should be chosen based on the problem domain, not the specifics of the underlying operating system. Microsoft’s COM and COM+ technology tried to fix this problem, but they were only successful to a certain degree, as their internal structures are quite convoluted. While COM and COM+ made great progress in bridging this domain gap, it just wasn’t the answer. This is one of the main reasons Microsoft proposed .NET, a new computing platform that simplifies application development in highly distributed environments.

Overview of .NET

There are two main components to Microsoft .NET: the Common Language Runtime and the .NET Class Framework. Microsoft.NET is based around the idea that code is in a managed environment; that is, it executes within a managed runtime (known as the Common Language Runtime, or CLR for short). The CLR acts as a barrier between managed applications (.NET) and the operating system. The CLR also offers a much richer set of services than normally provided by the operating system. The Common Language Runtime architecture is overviewed in Figure 2.1.

Overview of the Common Language Runtime architecture.

Figure 2.1. Overview of the Common Language Runtime architecture.

The Common Language Runtime manages code at execution time, providing core services such as memory management, thread management, and remoting. The CLR also enforces strict type safety and other forms of code accuracy that ensure security and robustness.

In order to have a language-independent CLR, a liaison is needed to facilitate the understanding of the language in the CLR. Every development tool for .NET compiles source code files to what is known as the Microsoft Intermediate Language, (MSIL, or IL for short), as shown in Figure 2.2.

Source code compilation into MSIL.

Figure 2.2. Source code compilation into MSIL.

All development tools produce the same MSIL regardless of the programming language, so all the CLR is required to do is understand the IL. Microsoft currently provides CLR-compliant versions of C#, Visual Basic, C++, JScript, and Java. Since any company can write a CLR-compliant language, third parties are introducing many others like COBOL, Delphi, Python, APL, and Perl.

The intermediate language code (IL) cannot run on its own. It must first be compiled by the Just-in-Time (JIT) compiler for the target platform to turn the IL into platform-specific machine-level code. This architecture provides Microsoft .NET with a certain level of platform independence. Work is currently being done by third parties to port the CLR to other platforms like UNIX and MacOS X.

The .NET platform also gives the capability to build durable system-level components thanks to the following features:

  • Robustness provided through type safety and garbage collection

  • Code security provided intrinsically through code trust mechanisms

  • Support for extensible meta-data concepts

  • Existing code integration support

  • Versioning to provide ease of administration and deployment

Full interoperability is also possible with other languages across multiple platforms, thanks to full XML support for web-based component interaction and COM+ support.

Aside from the Common Language Runtime, the other main component of the .NET platform is the Class Framework, which provides reusable functionality and technologies to any .NET compliant language and compiler.

Overview of C#

While there are a number of available languages supported by the .NET platform, C# is the most popular one for many reasons. The C# language is an elegant yet simple, type-safe, object-oriented language that allows for the development of a breadth of enterprise and highly distributed applications.

C# also provides access to the common API styles: COM+, Automation, .NET Class Framework, and C-style APIs. Also available is an unsafe mode, where pointers can be used when you want to manipulate memory that is not under control of the garbage collector.

The C# language is also an evolution of C++ and Java, and supports many of their features in the areas of expressions, statements, and operators. As a result, the learning curve for C# is generally quite rapid due to the comfort level when migrating from either C++ or Java.

Legacy Interoperability

Most game development studios have numerous legacy tools that do not have the available resources or need to migrate to the .NET platform. Microsoft realizes that migration does not magically happen overnight, and has provided some mechanisms to foster interoperability between managed and unmanaged components. The interoperability mechanisms permit developers to slowly migrate legacy components into managed applications piece by piece, while allowing them to build a complete application with a combination of unmanaged and managed components.

When building new .NET applications, there are provisions for using Win32 DLL exports and COM objects. There are also provisions for legacy applications to use a .NET assembly as if it were an ordinary COM object, and provisions to use an individual routine from a .NET assembly.

In addition to the interoperability mechanisms below, the .NET platform also includes support for Win32 sockets and Web Services, which can be utilized for interoperability between managed and unmanaged applications.

Platform Invocation Service (P/Invoke)

Interfacing with C-style functions in native DLLs is offered through the Platform Invocation Service, also known as P/Invoke, and although both Win32 API routines and custom exports are supported, the most common distinctive use is for accessing system routines that are not generally available to .NET developers. For example, when performing high-accuracy timing, you must use P/Invoke to call QueryPerformanceCounter and QueryPerformanceFrequency.

There is quite a varying degree of data types for both the Win32 and .NET platforms, and marshaling is required to transform data into the appropriate data types for each platform. The marshaling of parameters and return values between managed and unmanaged applications is handled through the Interop Marshaler, also used by COM Interop.

Platform Invocation Service is covered in much greater detail in Part V, “Techniques for Legacy Interoperability,” along with sample code on how to reference DLL exports in C#.

COM and Runtime Callable Wrappers

At some point you may need to interact with a COM object in a .NET application, and reconciliation between the .NET garbage collection model and the COM reference counting model is needed to allow both platforms to communicate with each other. In order for .NET to use a COM object, a Runtime Callable Wrapper (RCW) must be generated to cater to the differences between the lifetime management of .NET and COM objects. Runtime Callable Wrappers manage the reference counted lifetime of COM objects and also handle the marshaling of parameters and return types.

Additionally, .NET objects can also be exported to act like a COM object to use within a legacy application. This functionality is useful for applications that must remain unmanaged for the time being, but would benefit from the robustness of the .NET Class Framework.

Runtime Callable Wrappers and COM interoperability are covered in much greater detail in Part V along with sample code on how to use COM objects in .NET, and how to use .NET objects like COM objects in legacy applications.

C++/CLI (Managed Extensions for C++)

With such a following of developers using unmanaged C++ for application development, especially in the game development industry, there was a need for an enhancement to the C++ language that would allow programs written in C++ to use the .NET Class Framework and target the Common Language Runtime. It was for this reason that Microsoft created C++/CLI (formerly known as Managed Extensions for C++), an extension of the C++ language that could use the benefits from the .NET platform without requiring the user to learn a new programming language.

In other CLR languages like Visual Basic and C#, the only way to invoke Win32 API routines is through explicit use of the P/Invoke mechanism. Developers using C++/CLI do not need to use P/Invoke and can include the appropriate header files and call the unmanaged routines directly. This feature is called “It Just Works,” or IJW, and both P/Invoke and IJW use the same underlying mechanism so it is beneficial to understand that mechanism.

C++/CLI can also be used to wrap a C++ class or a COM object. Wrapping a COM object can provide better performance than using the COM interface and a Runtime Callable Wrapper because of reduced interoperability overhead, commonly referred to as “thunking.” It also allows for closer control of how members are wrapped.

For some COM objects, it may not be possible to use the Type Library Importer utility (tlbimp.exe) to generate an RCW for the COM object, and C++/CLI provides a solution to this problem.

Benefits

There are quite a number of benefits when the .NET platform is used for game engine tools development. Probably the largest benefit is the massive amount of productivity gain. Building applications in Microsoft.NET is much faster than any other RAD environment, because of the excellent IDEs available, as well as a very robust core framework that all managed applications can take advantage of. You can have a functional UI for simple tools created in under a couple of minutes, spending less time on UI and more time on functionality and usability. Being able to build a functional UI so quickly is very beneficial to a number of projects, most notably “throw-away” or “skunk works” tools that need a quick and dirty user interface, with the majority of the development time spent on building functionality.

Microsoft.NET also offers ease of deployment, solving the “DLL hell” agony. Through a built-in versioning mechanism available to all .NET assemblies, specific versions of a library can be targeted.

Other benefits are the promotion of scaleable architectures and the ability to choose architectures that are robust, reliable, and secure. Scaleable architectures promote reusability and strong design.

The interoperability support for legacy applications and components allows for easier migration from an existing code base to the .NET platform. A number of methods for bridging communication between managed and unmanaged applications exist, and these methods are covered in much greater detail later in the book.

Robustness is provided through type-safety and garbage collection. The compiler catches all invalid conversion operations and throws the appropriate exception. A .NET application can catch every error in the system, allowing for graceful error handling and termination. The only time when additional work must be done to ensure proper error handling is when an exception is thrown from a legacy application wrapped into a managed assembly. In addition to excellent error handling, .NET applications allocate and release memory through a reference counting garbage collector by default, ensuring that the application does not leak memory and the lifetimes of all objects are managed.

There are numerous other benefits when using .NET for tools, many of which will be covered in greater detail later in the book.

Conclusion

In reality, a game engine tool can be developed in many different languages: Perl, Python, CC++, Java, and Visual Basic, to name a few. So why use .NET? Tools enhance workflow and manage game content, so it is desirable to build these tools as quickly as possible. The faster a tool is developed, the sooner the end user can begin using it, improving productivity or producing game content earlier, most likely saving money or man hours in the process.

The .NET platform promotes robust design with a rapid application development nature, which is a perfect match for tools development. Many times a lot of utility functionality must be developed before the actual logic for the tool is addressed. The .NET framework provides countless functionality for technologies like XML, encryption, file system access, security, and data manipulation, to name a few. Development time for a tool can be better spent on logic and usability, rather than, on utility functionality that the tool is dependent on.

 

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.14.252.56