Introduction To
.Net
The
.NET platform is an integral component of the Microsoft Windows operating
system for building and running next generation software applications and Web
services. The .NET development framework provides a new and simplified model for
programming and deploying applications on the Windows platform. It provides
such advantages as multiplatform applications, automatic resource management,
and simplification of application deployment. As security is an essential part
of .NET, it provides security support, such as code authenticity check,
resources access authorizations, declarative and imperative security, and
cryptographic security methods for embedding into the user’s application.
.NET
provides a simple object-oriented model to access most of the Windows
application programming interfaces (APIs). It also provides mechanisms by which
you can use the existing native code. In addition, it significantly extends the
development platform by providing tools and technologies to develop Internet-based
distributed applications.
The .NET Platform

Basics of .Net Frame work
- The .NET Framework is a multilanguage, application
execution environment that transparently manages core infrastructure
services. It is a set of multiple languages/technologies used for
developing and creating components to create Web Forms, Web services, and
Windows applications. It supports the software life cycle for development,
debugging, deployment, and maintenance of applications. The version of
.NET framework that ships with Visual Studio .NET 2003 is version 1.1. The
.NET Framework consists of the following parts (also depicted in Figure
1.2):
- Common Language Runtime (CLR).
- .NET Framework base class library.
- Common Language Specification (CLS).
- .NET-compliant languages.
- Data and XML classes such as ADO.NET and XML.
- A set of class libraries for building XML Web
services.
- ASP.NET Web Forms-based Web applications.
- Windows Forms-based rich client applications.
- Common Type System (CTS).
- Microsoft Visual Studio .NET 2003 integrated
development environment (IDE).
- Development tools.
Microsoft provides the programming model, the development environment, and
the tools necessary to build, deploy, and operate Web services with
applications such as Visual Studio .NET 2003.
- .NET enterprise servers. The Microsoft .NET enterprise servers make up the
Microsoft .NET server infrastructure for deploying, managing, and
operating XML Web services and traditional applications. Examples of
enterprise servers are Microsoft SQL Server™ 2000 and Microsoft
Commerce Server 2000.
- .NET foundation services. A core set of building block services that execute
standard tasks and act as a basis for developers to build upon. These
foundation services are known as Microsoft .NET My Services and provide
many features and functions. Most of the foundation services are hosted
(outsourced) services. An example of a currently available Web service is
Microsoft .NET Passport.
The .NET Framework overview
Advantages
of .NET
The
.NET Framework provides the following advantages:
·
A consistent, object-oriented programming environment.
·
A code-execution environment that:
o
Promotes safe execution of code.
o
Eliminates the performance problems of scripted or interpreted
environments.
o
Minimizes software deployment and versioning conflicts.
·
A consistent experience for both developers and users across
various types of Windows-based and Web-based applications on multiple devices.
·
Communication built on the industry standards to ensure that
code based on the .NET Framework can integrate with any other code.
.NET
is based on open Internet standards, which include Hypertext Transfer Protocol
(HTTP), Extensible Markup Language (XML), and Simple Object Access Protocol
(SOAP).
Note More
information on XML is available at http://msdn.microsoft.com/xml/ .
More information on SOAP is available at
http://msdn.microsoft.com/library/en-us/dnsoap/html/understandsoap.asp.
More information on SOAP is available at
http://msdn.microsoft.com/library/en-us/dnsoap/html/understandsoap.asp.
Figure
1.2 depicts the overall architecture of the .NET Framework components.
Figure 1.2. The .NET Framework
architecture
Features of
the .NET Framework
This
section discusses some of the features of the Microsoft .NET Framework and how
to use these features in migrating code from the UNIX environment.
Common Language
Runtime (CLR)
The
core of the .NET Framework is the CLR, the run-time environment provided by
.NET. The runtime manages code at execution time and provides core services
such as memory management, thread management, remoting, and strict type safety
enforcement.
Figure
1.3 depicts the components of CLR.
Figure 1.3. CLR components
CLR Features
UNIX
applications, redeveloped on .NET, can make use of all the features provided by
the CLR, including:
·
Simplified development and deployment of applications.
·
Application memory management.
·
Improved performance, scalability, and reliability.
·
Cross-language inheritance.
·
Multiple language support.
·
Automatic garbage collection.
·
Security.
·
Strong type checking.
·
Access to type metadata.
·
Unified exception handling.
·
Interoperability with existing code in COM (Component Object
Model) objects and Microsoft Win32® DLLs.
·
Loading and executing code.
·
Just-in-time (JIT) compilation of Microsoft intermediate language
(MSIL) to native code.
·
Side-by-side execution for multiple assembly versions.
·
Other developer support services that include debugging and
run-time profiling.
·
Versioning and deployment support.
The
.NET runtime also enforces other forms of controlled code access that promote
security and robustness. Code management is a fundamental principle of the
runtime. Code that targets the CLR is known as managed code, whereas code that
does not target the CLR is known as unmanaged code. Unmanaged code can also be
used in the .NET environment using interoperability techniques as explained in
Chapter 3, “.NET Interoperability” of this volume.
All
.NET applications compile to a common language called MSIL. A JIT compiler then
compiles MSIL to optimized native code.
Benefits of CLR
This
various benefits offered by the CLR are as follows.
Security
The
runtime enforces code access security. The managed components have varied
degrees of trust level depending on a number of factors, including their
origin. Even if the same active application is using the managed component,
depending on the trust level, the managed component might or might not be
capable of performing file-access operations, registry-access operations, or
other sensitive functions.
For
example, users can trust that an executable embedded in a Web page can play an
animation or a song, but cannot access their personal data, file system, or
network.
Code
Robustness
The
runtime also enforces code robustness by implementing a strict
type-and-code-verification infrastructure called the Common Type System (CTS).
The CTS ensures that all managed code is self-descriptive. The various
Microsoft and third-party language compilers generate managed code that
conforms to the CTS.
Developer
Productivity
The
runtime also accelerates developer productivity by enabling the developers to
write applications in the development language of their choice and still take
advantage of all the features of the runtime. The language compilers that
target the .NET Framework make the features of the .NET Framework available to
the existing code written in that language, thus greatly facilitating the
migration process for the existing applications.
Performance
The
runtime is designed to enhance performance. JIT compiling enables all managed
code to run in the native code of the system on which it is executed. At the
same time, the memory manager removes the possibilities of fragmented memory
and increases the memory locality-of-reference to improve the performance
further.
Interoperability
The runtime,
although designed for modern software, also offers backward compatibility by
supporting older software. Interoperability between managed and unmanaged code
provides seamless integration to developers to continue to use necessary COM
objects and exported functions in unmanaged DLLs.
.NET Framework Base
Class Library
The
Microsoft .NET Framework 1.1 base class library is an object-oriented class
library providing an integrated set of classes that expose the underlying
functionality of the Win32 API as well as some other additional capabilities.
These classes integrate tightly with the CLR. Third-party components can also
integrate with the classes in the .NET Framework. In Microsoft .NET library,
all class (types) are grouped in namespaces. A
namespace is a grouping of similar kinds of classes.
The
.NET Framework classes enable you to perform a range of common programming
tasks such as string management, data collection, database connectivity, and
file access. In addition to these common tasks, the class library includes
classes that support a variety of specialized development functions. For
example, you can use the .NET Framework to develop a variety of applications
such as console applications, GUI (graphic user interface) applications, and
Web applications.
All
.NET languages can use these language-independent classes. This enables the
programmers to choose the language and tools best suited for the job or the
language with which they have the most experience and still share their code
and create new subclasses from classes written in a different language. This
code reuse can dramatically increase team productivity and decrease development
costs. Figure 1.4 depicts some of the namespaces and their classes in the .NET
Framework.
Figure 1.4. The .NET Framework
base class library
.NET Tools and
Technologies
From a
migration perspective, .NET provides various tools and technologies that help
you to migrate or redevelop a UNIX application on Windows. Some of these tools
and technologies are discussed in the following sections.
Database - ADO.NET
.NET
provides ADO.NET for migrating the database-related components of a UNIX
application to .NET. ADO.NET is a collection of classes, structures, and
interfaces that manage the data access for different databases. .NET provides
this data access technology to enable you to connect to different databases
including ODBC-aware (open database connectivity) databases.
Networking - System.NET
The System.NET namespace in .NET allows you to replicate
the networking functionality of a UNIX application on Windows.
Transaction - COM+ Services
.NET
provides COM+ services, also referred to as Enterprise Services in .NET, to
migrate applications that involve large amount of transactions.
Rich Client - Windows Forms
Windows
Forms enable you to replicate the X/Motif-based GUI of a UNIX application on
Windows. Windows Forms facilitate building of Windows rich-client applications
that take advantage of the CLR. The Visual Studio .NET 2003 IDE also aids in
the rapid redevelopment of GUI on Windows with the same look and feel as in
UNIX.
Web Applications - ASP.NET
.NET
provides Web Forms and ASP.NET for migrating the existing Web application on
UNIX to Windows. Web Forms and ASP.NET enable you to develop real-world Web
applications on Windows.
Application Integration - XML,
SOAP, and Web Services
.NET
supports XML, SOAP, Web services, and .NET servers that enable a migrated
application to integrate with the older applications and other applications in
your enterprise.
.NET XML Framework
This
section lists the XML APIs available in the .NET class library that you can use
for various XML-related operations.
XML is
truly a core technology substrate in .NET. All other parts of the .NET
Framework (such as ASP .NET and Web services) use XML as their native data
representation format. The .NET Framework XML classes are also tightly coupled
with Managed Data Access (ADO .NET). Traditionally, there have always been
different programming models for working with relational and hierarchical data.
.NET breaks that tradition by offering a more deeply integrated programming
model for all types of data.
New Suite
of XML APIs
Microsoft
.NET introduces a new suite of XML APIs built on such industry standards as
Document Object Model (DOM), XPath, XML Structured Definitions (XSD), and
Extensible Stylesheet Language Transformations (XSLT). The .NET Framework
XML classes offer convenience and better performance. The .NET XML Framework
also provides a more familiar programming model, tightly coupled with the
various classes present in System.Data and System.Xml namespaces, which encapsulate a number of
functionalities that previously had to be accomplished manually.
.NET XML
Namespaces
The
System.Xml assembly contains a broad range of general-purpose XML support
features, such as:
·
Basic I/O model.
·
I/O of primitive types.
·
In-memory traversal.
·
Filtering based on XPath expressions.
·
Transformations based on XSLT.
The
.NET XML stack is partitioned over several namespaces, such as:
·
System.Xml.XPath
·
System.Xml.Xsl
·
System.Xml.Schema
·
System.Xml.Serialization
XML-based I/O
All
XML-based I/O is performed using a streaming interface suite as follows:
·
Streams are supported in both pull-mode (read) and push-mode
(write).
·
Built-in streaming adapters use the System.IO.Stream class library.
·
Abstract interfaces allow you to provide your own XML
providers/consumers.
.NET DOM
Implementation
The
.NET DOM implementation (System.Xml.XmlDocument) supports all W3C DOM
Level 1 core and all DOM Level 2 core specifications, but with a few minor
naming changes. The DOM loading is built on top of XmlReader, while the DOM
serialization is built on XmlWriter. This makes it possible to extend how the
DOM interacts with applications in numerous ways.
Note More
information on W3C DOM Level 1 core and Level 2 core specifications is
available at http://www.w3.org/TR/1998/REC-DOM-Level-1-19981001/ and http://www.w3.org/TR/2000/REC-DOM-Level-2-Core-20001113/.
Transformations
The XslTransform class manages XSLT transformations in the
.NET Framework. XslTransform resides in the System.Xml.Xsl namespace and uses XmlNavigator during the transformation process. As with
all XSLT processors, XslTransform accepts an XML document, an XSLT document,
and some optional parameters as input. It can produce any type of text-based
output; it also supports reading the result of the transformation using a
custom XmlReader.
.NET Application Security
This
section provides an overview of various security models available in the .NET
Framework. The .NET Framework provides a rich security system, capable of
confining code to run in a tightly constrained, administrator-defined, security
context.
Role-based
Security
The
.NET Framework provides a developer-defined security model called role-based
security that attaches security to the users and their groups (or roles). The
principal abstractions of role-based security are principal and identity.
Code Access
Security
Additionally,
the .NET Framework also provides security on code, referred to as code access security (also referred to as evidence-based
security). With code access security, a user may be trusted to access a
resource but if the code that the user executes is not trusted, then access to
the resource is denied. Code access security also provides a highly protective
way of securing the assemblies from malicious attacks. Security based on code,
as opposed to specific user, is a fundamental facility that permits security to
be expressed on mobile code. Any number of users may download and execute
mobile code, which is unknown at the time of development. Code access security
focuses on some core abstractions, namely, evidence, policies, and permissions.
The
security abstractions for role-based security and code access security are
represented as types in the .NET Framework class library and are
user-extendable.
The
.NET Framework security system functions atop the traditional operating system
security. This adds a second, more expressive and extensible level to the
operating system’s security. Both layers complement each other. (It is
conceivable that an operating system security system can delegate some
responsibility to the CLR security system for managed code because the run-time
security system is more configurable then the traditional operating system security.)
Note More
information on .NET Framework security is available at http://msdn.microsoft.com/security/securecode/dotnet/default.aspx.
Implementation in .NET
Chapter
1, “Introduction to Win32/Win64” of Volume 3 has already discussed the platform
differences between UNIX and Windows from various aspects. The following topics
give an overview for implementing the following architectural elements in .NET:
·
Processes and threads
·
Memory management
·
File management
·
Signals, exceptions, and events
·
Networking
·
Interprocess communication
·
User interface
·
Daemons versus services
·
Deployment
Processes
and Threads
The
.NET Framework further divides an operating system process into lightweight
managed subprocesses, called application domains, which provide a versatile
unit of processing in .NET applications. These application domains are used to
provide isolation between applications and even within a single process.
Several application domains can be run in a single process with the same level
of isolation that would exist in separate processes. Historically, process
boundaries have been used to provide isolation between applications, but
application domains provide a level of isolation equivalent to that of a
process boundary, however at a much lower cost of performance.
The System.Threading namespace in .NET provides all the classes
and interfaces necessary to enable multithreaded programming. In addition to
classes for synchronizing thread activities and providing access to data (for
example, Mutex, Monitor, Interlocked, and AutoResetEvent), this namespace
includes a ThreadPool class that allows use of a pool of
system-supplied threads and a Timer class that executes callback methods on
the thread pool threads. The next chapters discuss application domains and the
threading namespaces in detail.
Memory
Management
The
garbage collector of the .NET Framework provides automatic memory management.
It allocates and releases the memory for managed objects and, when necessary,
executes the appropriate methods at the appropriate times in order to properly
clean up the unmanaged resources. Automatic memory management simplifies
development by eliminating the common bugs that arise from manual memory
management schemes.
File
Management
In
.NET, the System.IO namespace provides an object-oriented tool
to work with files and folders. It provides a collection of properties,
methods, and events to process text and data, thus enabling you to perform file
and directory operations with greater ease. For more information on the System.IO namespace, refer to Chapter 4, “Memory and
File Management” of this volume.
Signals,
Exceptions, and Events
.NET
Framework provides an event
handler mechanism
to handle events. An event handler is a procedure in your code that determines
the actions that must be performed when an event (such as the user clicking a
button or a message queue receiving a message) occurs. When an event is raised,
the event handler (or a handler) that receives the event is executed. Events
can be assigned to multiple handlers and the method that handles a particular
event can be changed dynamically.
The
.NET Framework handles exceptions through the exception handling mechanism. In the .NET Framework, an
exception is an object that it inherits from the System.Exception class. An exception originates from an
area of code where a problem occurred. The exception is passed up the stack
until the application handles it or the program terminates. All .NET languages
handle exceptions in a similar manner. Each language uses a form of
try/catch/finally structured exception handling.
Networking
The
.NET Framework class library includes two namespaces that consists of classes
that help you with networking; these are System.Net and System.Net.Sockets.
The System.Net classes provide a simple, yet complete
solution for writing networked applications in managed code. The System.Net.Sockets classes deals with the TCP/UDP and
sockets.
Interprocess
Communication
In the
.NET environment, application domains enable more than one application to run
within a single process, thus eliminating the overhead of making cross-process
calls but still maintaining the same level of application isolation that would
exist in separate processes. .NET also supports the concept of thread local
storage, by which data can be stored in a thread and accessed anywhere the
thread exists.
Microsoft
.NET Remoting provides a rich and extensible framework for objects residing in
different application domains, in different processes, and in different
computers to communicate with each other seamlessly. .NET Remoting offers a
powerful, yet simple, programming model and run-time support for making these
interactions transparent.
User
Interface
In the
.NET environment, user interfaces can be developed as Windows or Web Forms.
Some of the advantages of using these forms include the following:
·
Simplicity and power
·
Rich graphics
·
Flexible controls
·
Lower total cost of ownership
·
Architecture for controls
·
High security
·
XML Web services support
·
Data awareness
·
ActiveX control support
·
Easy licensing
·
Enhanced printing support
·
Accessibility
·
Design-time support
Daemons vs.
Services
The
.NET Framework class library includes the System.ServiceProcess namespace that provides classes to
implement, install, and control Windows service applications.
Services
are installed using an installation tool, such as InstallUtil.exe. The System.ServiceProcess namespace provides installation classes
that write service information to the registry.
The ServiceController class enables you to connect to an
existing service and manipulate it or get information about it. This class is
typically used in an administrative capacity; it enables you to start, stop,
pause, continue, or perform custom commands on a service.
Deployment
In
.NET Framework applications, assemblies are the building blocks. They form the
fundamental unit of deployment, version control, reuse, activation scoping, and
security permissions. Current Win32 applications have two versioning problems
with their building blocks (dynamic-link libraries):
·
Versioning rules cannot be expressed between pieces of an
application and enforced by the operating system.
·
Inability to maintain consistency between sets of components
that are built together and the set that is present at runtime.
These
two versioning problems combine to create DLL conflicts, or DLL Hell, where
installing one application can inadvertently break an existing application
because a certain software component or DLL that was installed was not fully
backward compatible with a previous version. The CLR uses assemblies to provide
a complete solution for DLL conflicts. Assemblies allow the runtime to:
·
Enable developers to specify version rules between different
software components.
·
Enable the infrastructure to enforce versioning rules.
·
Enable the infrastructure to allow multiple versions of a
component to run simultaneously (called side-by-side execution).
Each
computer, where the CLR is installed, has a machine-wide code cache called the
Global Assembly Cache (GAC). The GAC stores assemblies specifically designated
to be shared by several applications on the computer.
Assemblies
deployed in the GAC must have a strong name. When an assembly is added to the
GAC, integrity checks are performed on all files that make up the assembly. The
cache performs these integrity checks to ensure that an assembly has not been
tampered with.
Summary of
Platform Differences
Table
1.1 lists the basic platform differences that exist between UNIX, Windows, and
.NET.
Table
1.1. Summary of Platform Differences
Architectural
Element
|
UNIX
|
Windows
|
.NET
|
Processes
|
Processes
have a parent-child relationship.
Process
boundary provides isolation between applications.
|
Processes
do not have a parent-child relationship.
Process
boundary provides isolation between applications.
|
Processes
do not have a parent-child relationship.
An
operating system process is further divided into subprocesses called
application domains. Application domains provide isolation between
applications.
|
Threads
|
UNIX
threads are built upon the POSIX standard, known as Pthreads.
|
The
Windows operating system provides built-in support for threads and thread
synchronization using Platform SDK.
|
.NET
provides the System.Threading namespace to support
multithreading in conjunction with Windows threads.
|
Memory
|
At
application level, memory management is manual (in the hands of the
programmer).
|
At
application level, memory management is manual (in the hands of the
programmer).
|
Memory
management is automatic and controlled by the CLR, eliminating bugs such as
memory leakages from manual memory management.
|
File
|
File
operations are performed through low-level I/O functions and stream I/O
functions.
|
File
operations are performed through low-level I/O functions and stream I/O
functions.
|
The System.IO namespace provides an object-oriented
framework for handling files and folders.
|
Signals
|
UNIX
supports a wide range of signals. Signals are software interrupts that catch
or indicate different types of events.
|
Windows
supports only a small set of signals that is restricted to exception events.
Native signals, event objects, and messages are the recommended mechanisms to
replace common UNIX signals.
|
.NET
does not support signals.
Events
and exception handling mechanisms in .NET are the recommended mechanisms to
replace common UNIX signals.
|
Networking
|
UNIX
supports networking through sockets.
|
In
Windows, sockets are implemented using WinSock libraries.
|
The System.Net.Sockets namespace provides
networking support in .NET.
|
Interprocess
communication
|
UNIX
provides shared memory, pipes, and message queues for interprocess
communication.
|
Windows
provides shared memory, pipes, events, Dynamic Data Exchange (DDE), Component
Object Model (COM), and mailslots for interprocess communication.
|
.NET
Framework provides .NET Remoting and Microsoft Message Queue for interprocess
communication.
|
UI
differences
|
UI
is developed using X Windows and Motif, which are the standard windowing
system and windowing system library respectively on UNIX.
|
UI
is developed using MFC (Microsoft Foundation Classes), ATL (Active Template
Library), or GDI+ (Graphics Device Interface).
|
.NET
Framework provides Windows Forms and Web Forms for development of UI of rich
client and Web applications respectively.
|
Deployment
differences
|
UNIX
provides shared objects for developers to group common functionality and
deploy them.
|
Windows
provides DLLs for developers to group common functionality and deploy them.
|
In
.NET, assemblies form the fundamental unit of deployment, version control,
reuse, activation scoping, and security permissions.
|
Daemons
and services
|
UNIX
supports daemons (a process that runs in the background and does not require
a user interface).
|
Windows
service is the equivalent of daemons.
|
.NET
provides the System.ServiceProcess namespace to
implement, install, and control Windows service applications.
|
.NET Migration Paths
This
section discusses the various .NET migration paths. You can use this
information in reengineering code in .NET and in understanding the interaction
of the .NET code with existing applications. This section also helps you to
choose the best migration path based on the nature of existing UNIX
applications.
To
move UNIX applications to the Microsoft .NET Framework, you need to migrate the
existing UNIX C or C++ code to Windows. For example, if the UNIX applications
use third-party libraries and if the equivalents of such third-party libraries
are already available on Windows, then the UNIX to Windows move is much easier.
The code migrated from UNIX can then integrate the .NET Framework features.
For
UNIX applications involving code in Java, Visual Studio .NET 2003 provides a
tool known as Java Language Conversion Assistant (JLCA) that can automatically
convert existing Java language code into C#. You can use this tool in migration
scenarios where the existing UNIX application contains Java code.
With a
large code base of installed UNIX applications, you are unlikely to relish the
thought of throwing out the entire environment and starting again with an
unfamiliar platform. Fortunately, you do not necessarily have to do this. As
illustrated in this guide, methods are available by which you can preserve the
existing code while moving to .NET.
Analyzing
Application Types
The
application type and the ease with which you can move the application from UNIX
to Windows should decide whether to use the .NET interoperability strategies or
to redevelop the application completely on the .NET environment. Different
application types and strategies for migrating these application types are
discussed in the following sections.
Static Application
Static
applications are applications that are in the later stages of their life cycle,
with stable code, and with little or no changes planned. If these applications
can be migrated to Win32 using little or no code changes, then they can also be
adapted to make use of the capabilities of the .NET platform by using the
interoperability services provided by .NET. This way you can not only speed the
application migration but also preserve your investments in the existing code.
If the static application is an enterprise application, such as a portal or a
content management system, then you can take advantage of the capabilities of
the .NET servers, in addition to reusing the existing code.
Evolving Application
Evolving
applications are applications that are constantly being changed and enhanced.
An evolving application typically contains both static and dynamic components.
Static components are the parts of the application that do not change, whereas
dynamic components are the parts of the application that are evolving.
First,
identify the static and dynamic components of the application and then use the
.NET interoperability strategies to migrate the static components.
The
dynamic components can be redeveloped in .NET targeting the benefits offered by
the CLR and making full use of the other benefits of the .NET development
platform.
Table
1.2 lists the .NET migration strategies for the different types of
applications.
Table
1.2. Migration Strategies
Application
|
Nature
of Existing Application
|
Recommended
Solution
|
Static
application
|
Native
UNIX application (UNIX APIs, X Windows, and Motif).
|
Migrate
the application to Win32/Win64 with minor code changes and then interoperate
with the .NET code to preserve and make use of the investments made earlier.
|
Static
application
|
Enterprise
applications such as portal, content management, and others.
|
Use
.NET servers and interoperate with the migrated Win32/Win64 or unmanaged
code.
|
Evolving
application
|
Native
UNIX application (Unix APIs, X Windows, and Motif).
|
Reengineer
in .NET
|
Evolving
application
|
Enterprise
applications such as portal, content management, and others.
|
Use
.NET servers; interoperate with the migrated Win32/Win64 or unmanaged code,
and reengineer in .NET.
|
The
subsequent sections discuss the different migration strategies listed in Table
1.2.
·
Reengineering using the .NET Framework.
·
Interoperating with the existing code.
·
Utilizing .NET servers.
Reengineering
Using the .NET Framework
This
section describes reengineering the applications using the .NET Framework and
its advantages. A reengineering strategy is appropriate when an application
needs to evolve further and the costs of porting the application outweigh the
benefits, or when specific application qualities (such as performance or
scalability) require that the code be written specifically for the Windows
platform.
Rewriting
an application has a number of significant advantages, including:
·
Code robustness.
·
Accelerates developer productivity.
·
Enhanced performance and scalability.
·
Interoperability between managed code and unmanaged code.
·
Flexible language options.
·
Improved tool support.
·
Rich class library.
You
can redevelop an application using any of the .NET compatible languages, such
as Microsoft Visual Basic® .NET, C#, and Managed C++. Managed code, which is
compiled to Intermediate Language (IL) and not to machine code, is created
using these languages. The redeveloped code on .NET can make use of all the
features of the CLR. The code that is redeveloped in .NET to target the CLR
goes through the managed execution process. The following steps are involved in
the managed execution process.
1.
Choose a compiler.
To obtain the benefits
provided by the CLR, you must use one or more language compilers that target
the runtime, such as C#, Microsoft Visual C++®, Microsoft Visual Basic .NET,
Microsoft Visual J#®, Microsoft JScript®, or one of the many third-party
compilers such as Eiffel, Perl, or COBOL. Multiple .NET languages support a
common set of data types. For example, a string in C# is the same data type as
a string in Visual Basic .NET. If you are migrating a C++ application, consider
using the Visual C++ .NET for redevelopment. If you are migrating a Java
application, you can consider Visual C# for redevelopment.
2.
Compile code.
On compiling the code, it is
converted to Microsoft Intermediate Language (MSIL) code using the selected
.NET language compiler. When you execute the code for the first time, the
Just-In-Time (JIT) compiler translates MSIL into the native code. During this
compilation, code must pass a verification process that examines MSIL and the
metadata to find out whether the code can be determined to be type safe. There
are two kinds of JIT compilers: Standard JIT and Econo-JIT. Econo-JIT has a
faster compilation speed and a lesser compiler overhead than Standard-JIT. However,
the Standard-JIT generates more optimized code than the Econo-JIT and includes
the verification of MSIL code.
3.
Execute code.
The CLR provides the
infrastructure for executing the code as well as a variety of services that can
be used during execution.
Figure
1.5 illustrates the managed execution process in a .NET application.
Common Language Specification
To fully interact with other objects regardless of
the language they were implemented in, objects must expose to callers only
those features that are common to all the languages they must interoperate
with. For this reason, the Common Language Specification (CLS), which is a set
of basic language features needed by many applications, has been defined. The
CLS rules define a subset of the Common
Type System; that is, all the rules that apply to the common type system
apply to the CLS, except where stricter rules are defined in the CLS. The CLS
helps enhance and ensure language interoperability by defining a set of
features that developers can rely on to be available in a wide variety of
languages. The CLS also establishes requirements for CLS compliance; these help
you determine whether your managed code conforms to the CLS and to what extent
a given tool supports the development of managed code that uses CLS features.
IL code, CLR, CTS, CLS & JIT
IL/MSIL/CIL-
IL code is a CPU independent partially compiled code. It’s partially compiled
because we do not know in what kind of environment .Net code will run and on
runtime IL Code will compile to machine code using the environmental properties
(CPU, OS, machine configuration etc).
ILDASM-
this is tool provided by visual studio to view IL code. To run ILDASM we have
to select option “Visual Studio Command Prompt” from “Visual Studio Tools” and
type ildasm. It will open the ildasm tool where we can open any exe/dll.ildasm
tool read the assembly by reflection and it is showing us various properties,
methods which our assembly has. Here we can see IL code of any method/property
by clicking on that
CLR-
CLR is the heart of the .Net framework and it does 4 primary important things-
1.
Garbage collection
2.
CAS (Code Access Security)
3.
CV (Code Verification)
4.
IL to Native translation.
CTS-
CTS ensure that data types defined in two different languages get compiled to a
common data type. This is useful because there may be situations when we want
code in one language to be called in other language.
we
can see practical demonstration of CTS by creating same application in C# and
VB.Net and then compare the IL code of both application. Here the datatype of
both IL code is same.
CLS-
CLS is a subset of CTS. CLS is a set of rules or guidelines. When any
programming language adheres to these set of rules it can be consumed by any
.Net language.CTS
JIT-JIT
compiles the IL code to Machine code just before execution and then saves this
transaction in memory.
Assemblies can contain one or more modules. For example, larger projects may be planned in such a way that several individual developers work on separate modules, all coming together to create a single assembly. For more information about modules, see the topic How to: Build a Multifile Assembly.
Assemblies have the following properties:
Assembly Manifest
Within every assembly is an assembly manifest. Similar to a table of contents, the
assembly manifest contains the following:
Visual Basic and C# can only produce managed code, so, if you're writing an application in one of those languages you are writing an application managed by the CLR. If you are writing an application in Visual C++ .NET you can produce managed code if you like, but it's optional.
No free memory management or anything else the CLR provides.
Since you cannot create unmanaged code with Visual Basic or C#, in Visual Studio all unmanaged code is written in C/C++.
Here's an example I found by googling:
Example 1
Introduction
Background
What Exactly the DLLs and OCX files are?
ActiveX Components
Runtime Callable Wrappers
<formulas
/></formulas
/>

COM Callable Wrappers


.Net Platform Independent or not
.NET
is platform independent as long as the platform is .NET, the same way that Java
is platform independent as long as the platform is Java. That is to say, .NET
(and Java) are, in part, platforms in themselves (CLR, JVM). As long as there
is an implementation of that platform available for a given physical platform,
yes, you can run the compiled code on it independently.
Assemblies and the Global Assembly Cache
Assemblies form the fundamental
unit of deployment, version control, reuse, activation scoping, and security
permissions for a .NET-based application. Assemblies take the form of an
executable (.exe) file or dynamic link library (.dll) file, and are the
building blocks of the .NET Framework. They provide the common language runtime
with the information it needs to be aware of type implementations. You can
think of an assembly as a collection of types and resources that form a logical
unit of functionality and are built to work together.Assemblies can contain one or more modules. For example, larger projects may be planned in such a way that several individual developers work on separate modules, all coming together to create a single assembly. For more information about modules, see the topic How to: Build a Multifile Assembly.
Assemblies have the following properties:
·
Assemblies
are implemented as .exe or .dll files.
·
You
can share an assembly between applications by putting it in the global assembly
cache. Assemblies must be strong-named before they can be included in the
global assembly cache. For more information, see Strong-Named
Assemblies.
·
Assemblies
are only loaded into memory if they are required. If they are not used, they
are not loaded. This means that assemblies can be an efficient way to manage
resources in larger projects.
·
You
can programmatically obtain information about an assembly by using reflection.
For more information, see Reflection.
·
If
you want to load an assembly only to inspect it, use a method such as ReflectionOnlyLoadFrom.
Assembly Manifest
Within every assembly is an assembly manifest. Similar to a table of contents, the
assembly manifest contains the following:
·
The
assembly's identity (its name and version).
·
A
file table describing all the other files that make up the assembly, for
example, any other assemblies you created that your .exe or .dll file relies
on, or even bitmap or Readme files.
·
An assembly reference list, which is a list of all external
dependencies—.dlls or other files your application needs that may have been
created by someone else. Assembly references contain references to both global
and private objects. Global objects reside in the global assembly cache, an
area available to other applications, somewhat like the System32 directory. The
Microsoft.VisualBasic
namespace is an example of an assembly in the global assembly cache. Private
objects must be in a directory at either the same level as or below the
directory in which your application is installed.
Strong Name is a technology
introduced with the .NET platform and it brings many possibilities into .NET
applications. But many .NET developers still see Strong Names as security
enablers (which is very wrong!) and not as a technology uniquely
identifying assemblies. There is a lot of misunderstanding about SNs (as
we could see in the article "Building Security
Awareness in .NET Assemblies : Part 3 - Learn to break Strong Name .NET
Assemblies ") and this article attempts to clear those up.
Managed and Un Managed code
Managed code is not compiled to machine code but to an
intermediate language which is interpreted and executed by some service on a
machine and is therefore operating within a (hopefully!) secure framework which
handles dangerous things like memory and threads for you. In modern usage this
frequently means .NET but does not have to.
Unmanaged code is compiled to machine code and therefore
executed by the OS directly. It therefore has the ability to do
damaging/powerful things Managed code does not. This is how everything used to
work, so typically it's associated with old stuff like .dlls
Managed Code
Managed
code is what Visual Basic .NET and C# compilers create. It runs on the CLR
(Common Language Runtime), which, among other things, offers services like
garbage collection, run-time type checking, and reference checking. So, think
of it as, "My code is managed by the CLR."Visual Basic and C# can only produce managed code, so, if you're writing an application in one of those languages you are writing an application managed by the CLR. If you are writing an application in Visual C++ .NET you can produce managed code if you like, but it's optional.
Unmanaged Code
Unmanaged
code compiles straight to machine code. So, by that definition all code
compiled by traditional C/C++ compilers is 'unmanaged code'. Also, since it
compiles to machine code and not an intermediate language it is non-portable.No free memory management or anything else the CLR provides.
Since you cannot create unmanaged code with Visual Basic or C#, in Visual Studio all unmanaged code is written in C/C++.
Mixing the two
Since
Visual C++ can be compiled to either managed or unmanaged code it is possible
to mix the two in the same application. This blurs the line between the two and
complicates the definition, but it's worth mentioning just so you know that you
can still have memory leaks if, for example, you're using a third party library
with some badly written unmanaged code.Here's an example I found by googling:
#using <mscorlib.dll>
using namespace System;
#include "stdio.h"
void ManagedFunction(){
printf("Hello, I'm managed in this section\n");
}
#pragma unmanaged
UnmanagedFunction(){
printf("Hello, I am unmanaged through the wonder of IJW!\n");
ManagedFunction();
}
#pragma managed
int main()
{
UnmanagedFunction();
return 0;
}
Reflection
Reflection is the ability of a managed code to read
its own metadata for the purpose of finding assemblies, modules and type
information at runtime. In other words, reflection provides objects that
encapsulate assemblies, modules and types. A program reflects on itself by
extracting metadata from its assembly and using that metadata either to inform
the user or to modify its own behavior. Reflection is similar to C++ RTTI
(Runtime Type Information), but much broader in scope and capability. By using
Reflection in C#, one is able to find out details of an object, method, and
create objects and invoke methods at runtime. The
System.Reflection
namespace contains classes and interfaces that provide a managed
view of loaded types, methods, and fields, with the ability to dynamically
create and invoke types. When writing a C# code that uses reflection, the coder
can use the typeof operator to get the object's type or use the GetType()
method to get the type of the current
instance. Here are sample codes that demonstrate the use of reflection:
Example 1
Hide Shrink
Copy Code

using System;
using System.Reflection;
public class MyClass
{
public virtual int AddNumb(int numb1,int numb2)
{
int result = numb1 + numb2;
return result;
}
}
class MyMainClass
{
public static int Main()
{
Console.WriteLine ("\nReflection.MethodInfo");
// Create MyClass object
MyClass myClassObj = new MyClass();
// Get the Type information.
Type myTypeObj = myClassObj.GetType();
// Get Method Information.
MethodInfo myMethodInfo = myTypeObj.GetMethod("AddNumb");
object[] mParam = new object[] {5, 10};
// Get and display the Invoke method.
Console.Write("\nFirst method - " + myTypeObj.FullName + " returns " +
myMethodInfo.Invoke(myClassObj, mParam) + "\n");
return 0;
}
}
Firstly, the code snippet below will get the
type
information:
Hide Copy Code
Type myTypeObj = Type.GetType("MyClass");
And
myTypeObj
will now have the required information about MyClass
.
Therefore we can now check if the class is an abstract
class or a regular class by using either of these statements:
Hide Copy Code
myTypeObj.IsAbstract
or:
Hide Copy Code
myTypeObj.IsClass
The code snippet below will get the method's
information. And the method that we are interested in this case is
AddNumb
:
Hide Copy Code
Methodinfo myMethodInfo = myTypeObj.GetMethod("AddNumb");
The
following code snippet will invoke the
AddNumb
method:
Hide Copy Code
myMethodInfo.Invoke(myClassObj, mParam);
//Example2: In this example, we will use the typeof keyword to obtain the
// System.Type object for a type.
Public class MyClass2
{
int answer;
public MyClass2()
{
answer = 0;
}
public int AddNumb(intnumb1, intnumb2)
{
answer = numb1 + numb2;
return answer;
}
}
The code snippet below gets the
System.Type
object for the MyClass2 type
.
Hide Copy Code
Type type1 = typeof(MyClass2);
So we will now be able to create an instance of the
type1
object by passing the
type1
object to the Activator.CreateInstance(type1)
method.
Hide Copy Code
object obj = Activator.CreateInstance(type1);
Then we can now invoke the
AddNumb
method of the MyClass2
class by first creating an array of
objects for the arguments that we would be passing to the AddNumb(int, int)
method.
Hide Copy Code
object[] mParam =newobject[] {5, 10};
Finally, we would invoke the
AddNumb(int, int)
method by passing the method
name AddNumb
to System.Type.InvokeMember()
with the appropriate
arguments.
Hide Shrink
Copy Code

int res = (int)type1.InvokeMember("AddNumb", BindingFlags.InvokeMethod,null,
obj, mParam);
//Here is the complete code:
using System;
using System.Reflection;
namespace Reflection
{
class Class1
{
public int AddNumb(int numb1, int numb2)
{
int ans = numb1 + numb2;
return ans;
}
[STAThread]
static void Main(string[] args)
{
Type type1 = typeof(Class1);
//Create an instance of the type
object obj = Activator.CreateInstance(type1);
object[] mParam = new object[] {5, 10};
//invoke AddMethod, passing in two parameters
int res = (int)type1.InvokeMember("AddNumb", BindingFlags.InvokeMethod,
null, obj, mParam);
Console.Write("Result: {0} \n", res);
}
}
}
Using COM Components in .NET
Introduction
This Article will help you in
understanding the Inter operation between .NET Environment and COM components
(Which are nothing but Win32 Dynamic Linked Library and ActiveX controls.
That's .dll and .ocx files).
Background
Consider this, If I have few COM
components which includes DLLs and ActiveX components as well, and now time has
come to upgrade the application functionality and UI things, Also there is
requirement of Application availability through Web. So what I have decided to
write some part of the application in .NET so that Web Deployment and the
registration problems of DLL and ActiveX problems will be removed.
In above scenario, either I can
write the whole new application which is truly .NET Framework based, or I can
make use of the available functionality of the business logic in newly
developed Web Application and Desktop application. Yes, I am talking about the
COM-.NET Inter Operation, the way by which I we can talk with COM components
and vice versa.
Let's start digging and find out
what exactly the COM Inter Op is and what are the different ways by which we
can achieve it.
What Exactly the DLLs and OCX files are?
A Dynamic Linked Library (DLL)
is a file code containing functions that can be called from other executable
code (either an application or another DLL). Programmers use DLLs to provide
code that they can reuse and ship with the product and to distribute. Unlike an
executable (EXE) file, a DLL cannot be directly run. DLLs must be called from
other code that is already executing.
A DLL is a file indicated by the
extension DLL in its name.
e.g- > kernell32.dll,
msvbvm60.dll, msvcrt32.dll
In Simple words DLL is a file
which contains function calls available for the outside world and outside world
can make use of these function calls. Here outside world is nothing but,
Another DLL and or an Application.
On the other hand OCX or simply
ActiveX components are the components which (Dlls and Ocx are together called
as ActiveX components). In next section we will see what exactly the ActiveX
Components are.
ActiveX Components
An ActiveX control is really
just another term for "OLE Object" or, more specifically,
"Component Object Model (COM) Object." In other words, a control, at
the very least, is some COM object that supports the IUnknown interface and is
also self-registering. Through QueryInterface a container can manage the
lifetime of the control, as well as dynamically discover the full extent of a
control's functionality based on the available interfaces. This allows a
control to implement as little functionality as it needs to, instead of
supporting a large number of interfaces that actually don't do anything. In
short, this minimal requirement for nothing more than IUnknown allows any
control to be as lightweight as it can. Other than IUnknown and
self-registration, there are no other requirements for a control. There are,
however, conventions that should be followed about what the support of an
interface means in terms of functionality provided to the container by the
control. This section describes what it means for a control to actually support
an interface, as well as methods, properties, and events that a control should
provide as a baseline if it has occasion to support methods, properties, and
events.
I think, this is enough to see
what and how exactly we can interact with COM from within the .NET and vice
versa.
Runtime Callable Wrappers
As discussed in background, it
is more likely that.NET Application will have to interoperate with COM, we will
first find out how we can achieve that. A .NET application access COM through
Runtime callable wrappers or RCW. RCW wraps COM object and acts as a bridge
between COM and .NET Runtime, and acts as if RCW is a Native Object.
We can generate RCW, by one of
the two ways. First, using Visual Studio.NET, In Microsoft Visual Studio.NET,
right-click on the References section of your Project, (If you are unable to
view the References section under project, then click on "Show All
Files" icon on Solution Explorer window, by default References section is
hidden in Visual Studio 2005). Select Add Reference, you will see the dialog
box, (Fig.1) here you can browse for COM component that you want add into your
project as a reference. Select the desired file and click on add, Visual
Studio.NET will create RCW and will add it as a reference in the project.

(Fig.1.)
If you're not using Microsoft
Visual Studio.NET Environment, there is another way bu which you can create
RCW. When you install .NET Framework SDK (VS.NET), there are certain set of
tools, gets installed on the system. Out of those tools, one tools called
TlbImp.exe (Type Lib Importer Utility), which is useful for creating RCW or
Runtime callable Wrappers. Let's find out how we can actually import COM as
reference or how we can create RCW.
(Note: To run this example make
sure you have Library path set in your system variables.)
For example you want to add
COMDemo.dll Com component from command line as a reference, then on the command
line you type following.
C:\> tlbimp comdemo.dll
It will create RCW in the
directory you've specified; now you can refer RCW in your project by importing
or creating objects.
COM Callable Wrappers
Suppose, on the other hand, that
you have a client that already talks with COM and now you want to make it use a
.NET object instead. This is a somewhat less common scenario than the reverse
situation because it presupposes new COM development in a .NET world. But I can
easily see it occurring if you have an existing COM client that uses 10 COM
objects and you now want to add an additional set of functionality that exists
only as a .NET object. The .NET Framework supports this situation as well, via
a COM-callable wrapper (CCW).
Here exactly the reverse things
happens, CCW wraps .NET Objects and makes it available for the COM interaction,
it acts as if it is a COM Component to the Other COM objects and or application
outside of the .NET world. Now there are some rules that we need to follow to
make it happen and work properly. First, .NET component which is required to be
exposed to the COM, must have signed strong name, otherwise CLR will not
recognize it. Second, it must reside in local machines Global Assembly Cache
(GAC). If you want your CCW to be available for the other applications also,
then it must reside in GAC, if not (which is less common) then it must present
in applications directory (then it will act as Private assembly and is only
available to that particular application). As the matter of fact COM component
does not know how to pass the parameters while creating an object, .NET
component, which is to be exposed must have a default constructor (a
constructor without parameter list).
In order to for COM components
to find .NET Object, you also need to make registry entry for that .NET
component. This we can do using a tool call RegAsm.exe, which is again
available when you install .NET SDK. To register type following on the command
line.
C:\> regasm <name of the
.NET component>
After registration is
successful, COM Components can take the advantages of .NET objects.
In next article we will look
more deeply, how we can create .NET Components for COM interaction and how we
can control the exposure.
Here is something useful which you
can take advantage of.
1)
Registering DLL,OCX using Win32 command line. In Start-> Run Type following
if you want to register any dll or ocx file. Regsvr32 "path"\xyz.dll
or Regsvr32 /i path"\xyz.dll
2) To un
registering the registered path from the windows registry you type Regsvr32 /u
path"\xyz.dll
Net Interoperability ( COM, CCW,RCW , regsvr32 )
One of the big challenges I had to overcome this week was how to
launch an application with arguements from a hyperlink in the WebBrowser
Control. You would think this would be fairly easy, but underneath the
workings of the WebBrowser Control is security-heavy Internet Explorer. I
guess one of Microsoft's ongoing challenges is to balance the amount of
security IE needs against viruses and wormbots and to make the control useable
for developers. It seems to me, when it comes to automating IE, Microsoft
has lately been leaning on making life more secure for the consumer and more
painful for developers. I guess my mistake was to think I could use the
WebBrowser Control as a Reporting Tool and extend it beyond simple
reporting. Still, it would be REALLY nice if there was a way to turn off
the security for the WebBrowser control when you are developing it to be used
as an application on a local machine.
Anyway, after three days of research and some hard learning
experience, I managed to figure out how you could launch an application from a
hyperlink in HTML. The trick is to write javascript into your HTML report
that will create an ActiveX control that will in turn call the application you
wish to launch.

Figure
1 - Steps for running an executable from a hyperlink in IE
Javascript from a Hyperlink
Did you know that you can launch javascript from a hyperlink
using the following syntax?
<a href = "javascript:alert('hello')">
post alert</a>
|
You can try it yourself by just typing
javascript:alert('hello')
|
into your browser, which brings up figure 2:

Figure
2 - Result of javascript command in browser edit window
The same technique can be used to call your own javascript
function called launch, that will eventually launch your local machine process:
<a href = "javascript:launch('name',
'date')">launch it!</a>
|
In my current project, I needed to generate the line above for a
report using xsl. If you are generating the hyperlink above from xsl,
your xslt for this might look like the following (xsl gives you the flexibility
of populating the parameters in your javascript call with data from an xml
file.)
Listing 1 - XSL for generating a hyperlink
that calls javascript
<A>
<xsl:attribute name="HREF"> javascript:launch('<xsl:value-of select ="$name"/>', '<xsl:value-of select ="$date"/>') </xsl:attribute> <xsl:value-of select ="$title"/> </A> |
So what does the javascript launch code look like? The
javascript just gives us a conduit to an ActiveX control which we can call with
the parameters name and date. We simply use the javascript to create the
ActiveX COM object and dispatch a Run call from the object.
Listing 2 - javascript for creating and
calling an ActiveX Object
<script type="text/javascript">
function launch(name, date) { var myLauncher = new ActiveXObject('HyperLinkLauncher.Launcher'); alert ("name:" + name + " date:" + date); myLauncher.Run(name, date); } </script>
If we want to include the javascript in our xsl generation
script, we can just stick the javascript all in a CDATA block so xsl won't get
confused by the javascript symbols and characters:
Listing 3 - xsl for generating the
javascript in listing 2
Creating an COM Callable Wrapper (CCW) in .NET
Now we have all the pieces in our HTML to call an ActiveX
Control with parameters from HTML. The next step is to create the ActiveX
control that will launch a process on our local machine. Microsoft gives
us an easy way to create ActiveX controls in .NET using the COM Callable
Wrapper. By just placing a few attributes in our classes and interfaces,
we can add automation directly into a .NET program or library. (It sure beats
the old way using ATL!!)
So let's review what we are trying to do. We are trying to
launch a process from the hyperlink and pass it parameters. In order to
launch the process we need to create an ActiveX Control called HyperLinkLauncher.Launcher.
The first step to creating are ActiveX COM callable wrapper is just to create a
new project of type library. ActiveX controls don't need to be
executables, we just need to be able to create them and use their an interface
to access their methods and properties. Go to the File->New->Project
menu and choose a Class Library project as shown in figure 2.
![]()
Figure
2 - Creating a C# Project of Type Library
Next we open up the Launcher class and add a ProgId attribute to
the class named "HyperLinkLauncher.Launcher". Adding this
attribute will allow us to create a COM object with this name using
javascript. Also we want to add a Run method to run our command line
application. The Run method uses the System.Diagnostics.Process.Start
method to launch our commandline process with the name and date parameters.
Listing 4 - Adding the ProgId attribute
In
order to access our run method through COM, we need to add an interface.
This interface will be used to create our dispatch interface which will expose
our COM methods and properties. The interface is shown in listing
5. The InterfaceType attribute and the DispID attribute adds the
plumbing to create the necessary COM functionality: Also you need a
GUID (unique id) attribute, to distinguish your ActiveX controls from all the
other activex controls on your computer system. You can generate the GUID
from the VS.NET menu under Tools->Create GUID and then paste the GUID
into the Guid attribute above your interface.
Listing 5 - Adding the ProgId attribute
The full code for your the CCW is shown in Listing 6. Note
that there really isn't that much coding necessary to turn your .NET class into
a COM object.
Listing 6- A COM Callable Wrapper for
Launching a Process with Arguements
Installing the COM Callable Wrapper
It's not enough to build the CCW and celebrate your successful
hack. In order to get your COM callable wrapper to work properly,
three things need to happen (1) Your CCW must be strong named (2)
you need put your library assembly into the GAC. (3) You need to
register your Active X control as a COM object.
In order to create a strong name for your assembly, go to your
project properties in Visual Studio, pick the signing tab, and check Sign
the assembly. Also, you will need to provide a strong name key file
name.
![]()
Now we need to place the signed assembly in the GAC. The
easiest way to stick the CCW into the GAC is to open up c:\windows\assembly under file
explorer and drag your compiled dll into the directory. Of course, you
could also use the command line tool gacutil.exe.
gacutil.exe /i Launcher.dll
To register your COM callable wrapper with your system,
you'll need to use regasm (regsvr32.exe won't work on a .NET CCW).
Upon running regasm, the utility will indicate if your dll was registered
successfully. You can also use visual studio 6.0 tools such as OLE
View to look at the contents of Launcher.tlb after it is generated to make
sure that your COM interface was created properly. The command line for
regasm is (including type lib generation) is shown below:
regasm /tlb:Launcher.tlb Launcher.dll
Once you've successfully registered your COM object, you can
test your javascript code to make sure it launches your app properly. You
may want to add a MessageBox.Show (you'll need to include
System.Windows.Forms.dll in your library references.) in your Run method
to make sure your ActiveX Run command is being accessed.
Conclusion
Creating COM callable objects is a snap using the attributes
provided with the .NET framework. One of the great uses for these CCW's
(along with javascript) is to allow you to launch applications from the
WebBrowser control. Unfortunately, you will still initially see the
warning in the WebBrowser control that says, "An active x control
might be unsafe...". I have not yet figured out how to get rid of
this annoying message. If you have any suggestions, feel free to contact
me and I'll add it to this article. Fortunately, the message disappears
once you say Yes, but will come back next time you run the application.
![]()
Figure
3 - Warning that comes up in the WebBrowser Control
Anyway, hopes this help you utilize the WebBrowser control
as a more interactive part of your .NET arsenal. Remember, if you have
any COMments, please feel free to share them here on C# Corner.
C# 2.0 Features
First off, generics are not like C++ templates. They
primarily provide for strongly typed collections.
Hide Copy Code
public void WithoutGenerics()
{
ArrayList list = new
ArrayList();
// ArrayList is of
type object, therefore essentially untyped.
// Results in boxing
and unboxing of value types
// Results in
ability to mix types which is bad practice.
list.Add(1);
list.Add("foo");
}
Without generics, we incur a "boxing" penalty
because lists are of type "object", and furthermore, we can quite
easily add incompatible types to a list.
Hide Copy Code
public void WithGenerics()
{
// Generics provide
for strongly typed collections.
List<int> list
= new List<int>();
list.Add(1); //
allowed
//
list.Add("foo"); // not allowed
}
With generics we are prevented from using a typed collection
with an incompatible type.
Generics can also be used in non-collection scenarios, such
as enforcing the type of a parameter or return value. For example, here we
create a generic method (the reason we don't create a generic MyVector will be
discussed in a minute:
Hide Shrink
![]()
public class MyVector
{
public int X { get;
set; }
public int Y { get;
set; }
}
class Program
{
public static T
AddVector<T>(T a, T b)
where T :
MyVector, new()
{
T newVector = new
T();
newVector.X =
a.X + b.X;
newVector.Y =
a.Y + b.Y;
return
newVector;
}
static void Main(string[]
args)
{
MyVector a = new
MyVector();
a.X = 1;
a.Y = 2;
MyVector b = new
MyVector();
b.X = 10;
b.Y = 11;
MyVector c =
AddVector(a, b);
Console.WriteLine(c.X + ", " + c.Y);
}
}
Notice the constraint. Read more about constraints here.
The constraint is telling the compiler that the generic parameter must be of
type MyVector, and that it is an object (the "new()") constraint,
rather than a value type. The above code is not very helpful because it would
require writing an "AddVector" method for vectors of different types
(int, double, float, etc.)
What we can't do with generics (but could with C++
templates) is perform operator functions on generic types. For example, we
can't do this:
Hide Copy Code
public class MyVector<T>
{
public T X { get; set;
}
public T Y { get; set;
}
// Doesn't work:
public void
AddVector<T>(MyVector<T> v)
{
X = X + v.X;
Y = Y + v.Y;
}
}
This results in a "operator '+=' cannot be applied to
operands of type 'T' and 'T'" error! More on workarounds for this later.
You might see generics used in factories. For example:
Hide Copy Code
public static T Create<T>() where T : new()
{
return new T();
}
The above is a very silly thing to do, but if you are
writing an Inversion of Control layer, you might be doing some complicated
things (like loading assemblies) based on the type the factory needs to create.
Partial types can be used on classes, structs, and
interface. In my opinion, partial types were created to separate out tool
generated code from manually written code. For example, the Visual Studio form
designer generates the code-behind for the UI layout, and to keep this code
stable and independent from your manually written code, such as the event
handlers, Visual Studio creates two separate files and indicates that the same
class is of partial type. For example, let's say we have two separate files:
File 1:
Hide Copy Code
public partial class MyPartial
{
public int Foo { get;
set; }
}
File 2:
Hide Copy Code
public partial class MyPartial
{
public int Bar { get;
set; }
}
We can use the class, which has been defined in two separate
files:
Hide Copy Code
public class PartialExample
{
public MyPartial
foobar;
public
PartialExample()
{
foobar.Foo = 1;
foobar.Bar = 2;
}
}
Do not use partial classes to implement a
model-view-controller pattern! Just because you can separate the code into
different files, one for the model, one for the view, and one view the
controller, does not mean you are implementing the MVC pattern correctly!
The old way of handling tool generated code was typically to
put comments in the code like:
Hide Copy Code
// Begin Tool Generated Code: DO NOT TOUCH
... code ...
// End Tool Generated Code
And the tool would place its code between the comments.
Anonymous methods let us define the functionality of a
delegate (such as an event) inline rather than as a separate method.
Before anonymous delegates, we would have to write a
separate method for the delegate implementation:
Hide Copy Code
public class Holloween
{
public event
EventHandler ScareMe;
public void OldBoo()
{
ScareMe+=new
EventHandler(DoIt);
}
public void Boo()
{
ScareMe(this,
EventArgs.Empty);
}
public void DoIt(object
sender, EventArgs args)
{
Console.WriteLine("Boo!");
}
}
With anonymous methods, we can implement the behavior
inline:
Hide Copy Code
public void NewBoo()
{
ScareMe += delegate(object
sender, EventArgs args) { Console.WriteLine("Boo!"); };
}
We can do the same thing with the Thread class:
Hide Copy Code
public void AsyncBoo()
{
new Thread(delegate()
{ Console.WriteLine("Boo!"); }).Start();
}
Note that we cast the method as a
"delegate()"--note the '()'--because there are two delegate forms and
we have to specify the parameterless delegate form.
My favorite example is calling the main application thread
from a worker thread to update a UI component:
Hide Copy Code
/// <summary>
/// Called from some async process:
/// </summary>
public void ApplicationThreadBoo()
{
myForm.Invoke((MethodInvoker)delegate { textBox.Text = "Boo";
});
}
Iterators reduce the amount of code we have to write to
iterate over a custom collection.
Previous to C# 2.0, we had to implement the IEnumerator
interface, supplying the Current, MoveNext, and Reset operations manually:
Hide Shrink
![]()
public class DaysOfWeekOld : IEnumerable
{
protected string[]
days = new string[] { "Monday", "Tuesday", "Wednesday",
"Thursday",
"Friday",
"Saturday", "Sunday" };
public int Count { get
{ return days.Length; } }
public string this[int
idx] { get { return days[idx]; } }
public IEnumerator
GetEnumerator()
{
return new
DaysOfWeekEnumerator(this);
}
}
public class DaysOfWeekEnumerator : IEnumerator
{
protected
DaysOfWeekOld dow;
protected int pos =
-1;
public
DaysOfWeekEnumerator(DaysOfWeekOld dow)
{
this.dow = dow;
}
public object
Current
{
get { return
dow[pos]; }
}
public bool
MoveNext()
{
++pos;
return (pos <
dow.Count);
}
public void Reset()
{
pos = -1;
}
}
In the new approach, we can use the yield keyword to iterate
through the collection:
Hide Copy Code
public class DaysOfWeekNew : IEnumerable
{
protected string[]
days = new string[] { "Monday", "Tuesday", "Wednesday",
"Thursday",
"Friday", "Saturday",
"Sunday" };
public IEnumerator
GetEnumerator()
{
for (int i = 0; i
< days.Length; i++)
{
yield return
days[i];
}
}
}
This is much more readable and also ensures that we don't
access elements in the collection beyond the number of items in the collection.
We can also implement a generic enumerator, which provides a
type safe iterator, but requires us to implement both generic and non-generic GetEnumerator
method:
Hide Copy Code
public class DaysOfWeekNewGeneric : IEnumerable<string>
{
protected string[]
days = new string[] { "Monday", "Tuesday", "Wednesday",
"Thursday",
"Friday",
"Saturday", "Sunday" };
IEnumerator
IEnumerable.GetEnumerator()
{
return
Enumerate();
}
public
IEnumerator<int> GetEnumerator()
{
return
Enumerate();
}
public
IEnumerator<string> Enumerate()
{
for (int i = 0; i
< days.Length; i++)
{
yield return
days[i];
}
}
}
So, for example, in the non-generic version, I could write:
Hide Copy Code
DaysOfWeekNew dow2 = new DaysOfWeekNew();
foreach (string day in dow2)
{
Console.WriteLine(day);
}
which is perfectly valid, but I could also write:
Hide Copy Code
DaysOfWeekNew dow2 = new DaysOfWeekNew();
foreach (int day in dow2)
{
Console.WriteLine(day);
}
The error in casting from a string to an integer is caught
at runtime, not compile time. Using a generic IEnumerable<T>, an improper
cast is caught at compile time and also by the IDE:
Hide Copy Code
DaysOfWeekNewGeneric dow3 = new DaysOfWeekNewGeneric();
foreach (int day in dow3)
{
Console.WriteLine(day);
}
The above code is invalid and generates the compiler error:
"error CS0030: Cannot convert type 'string' to
'int'"
Thus, the implementation of generic iterators in C# 2.0
increases readability and type safety when using iterators.
Nullable types allow a value type to take on an additional
"value", being "null". I've found this primarily useful
when working with data tables. For example:
Hide Shrink
![]()
public class Record
{
public int ID { get;
set; }
public string Name {
get; set; }
public int? ParentID
{ get; set; }
}
public class NullableTypes
{
protected DataTable
people;
public NullableTypes()
{
people = new
DataTable();
// Note that I am
mixing a C# 3.0 feature here, Object Initializers,
// with regards to
how AllowDBNull is initialized. I'm doing because I think
// the example is
more readable, even though not C# 2.0 compilable.
people.Columns.Add(new DataColumn("ID", typeof(int))
{AllowDBNull=false});
people.Columns.Add(new DataColumn("Name", typeof(string)) {
AllowDBNull = false });
people.Columns.Add(new DataColumn("ParentID", typeof(int)) {
AllowDBNull = true });
DataRow row =
people.NewRow();
row["ID"]
= 1;
row["Name"]
= "Marc";
row["ParentID"]
= DBNull.Value; // Marc does not have a parent!
people.Rows.Add(row);
}
public Record
GetRecord(int idx)
{
return new
Record()
{
ID =
people.Rows[idx].Field<int>("ID"),
Name =
people.Rows[idx].Field<string>("Name"),
ParentID =
people.Rows[idx].Field<int?>("ParentID"),
};
}
}
In the above example, the Field extension method (I'll
discuss extension methods later) converts DBNull.Value automatically to a
"null", which in this schema is a valid foreign key value.
You will also see nullable types used in various third party
frameworks to represent "no value." For example, in the DevExpress
framework, a checkbox can be set to false, true, or no value. The reason for
this is again to support mapping a control directly to a structure that backs a
table with nullable fields. That said, I think you would most likely see
nullable types in ORM implementations.
A private setter exposes a property as read-only, which is
different from designating the property as readonly. With a field designated as
readonly, it can only be initialized during construction or in the variable
initializer. With a private setter, the property can be exposed as readonly to
the outside world the class implementing the property can still write to it:
Hide Shrink
![]()
public class PrivateSetter
{
public int readable;
public readonly int
readable2;
public int Readable
{
get { return
readable; }
// Accessible only
by this class.
private set {
readable = value; }
}
public int Readable2
{
get { return
readable2; }
// what would the
setter do here?
}
public
PrivateSetter()
{
// readonly fields
can be initialized in the constructor.
readable2 = 20;
}
public void Update()
{
// Allowed:
Readable = 10;
// Not allowed:
// readable2 = 30;
}
}
Contrast the above implementation with C# 3.0's
auto-implemented properties, which I discuss below.
I must admit to a "what the heck is this?"
experience for this feature. First (for my education) a "method
group" is a set of methods of the same name. In other words, a method with
multiple overloads. This post
was very helpful. I stumbled across this post
that explained method group conversion with delegates. This also appears to
have to do with covariance and contravariance, features of C# 4.0. Read more here.
But let's try the basic concept, which is to assign a method to a delegate
without having to use "new" (even though behind the scenes, that's
apparently what the IL is emitting).
Hide Copy Code
public class MethodGroupConversion
{
public delegate string
ChangeString(string str);
public ChangeString
StringOperation;
public
MethodGroupConversion()
{
StringOperation = new
ChangeString(AddSpaces);
}
public string Go(string
str)
{
return
StringOperation(str);
}
protected string
AddSpaces(string str)
{
return str + "
";
}
}
We replace the constructor with a more straightforward
assignment:
Hide Copy Code
public MethodGroupConversion()
{
StringOperation =
AddSpaces;
}
OK, that seems simple enough.
The "var" keyword is a new feature of C# 3.0.
Using the "var" keyword, you are relying on the compiler to infer the
variable type rather than explicitly defining it. So, for example, instead of:
Hide Copy Code
public void Example1()
{
// old:
Dictionary<string,
int> explicitDict = new Dictionary<string, int>();
// new:
var implicitDict = new
Dictionary<string, int>();
}
While it seems like syntactical sugar, the real strength of
implicit types is its use in conjunction with anonymous types (see below.)
Note the phrase "local variables" in the heading
for this section. Implicitly typed variables cannot be passed to other methods
as parameters nor returned by methods. As Richard Deeming commented below, what
I mean by this is that you cannot specify var as a parameter or return type,
but you can call a method with an implicit type of the method's parameter is an
explicit type, and similarly (and more obviously) with return parameters -- an
explicit return type can be assigned to a var.
Previously, to initialize property values from outside of
the class, we would have to write either use a constructor:
Hide Copy Code
public Record(int id, string name, int? parentID)
{
ID = id;
Name = name;
ParentID = parentID;
}
...
new Record(1, "Marc", null);
or initialize the properties separately:
Hide Copy Code
Record rec=new Record();
rec.ID = 1;
rec.Name = "Marc";
rec.ParentID = null;
In its explicit implementation, this simply allow us to
initialize properties and collections when we create the object. We've already
seen examples in the code above:
Hide Copy Code
Record r = new Record() {ID = 1, Name = "Marc",
ParentID = 3};
More interestingly is how this feature is used to initialize
anonymous types (see below) especially with LINQ.
Similarly, a collection can be initialized inline:
Hide Copy Code
List<Record> records = new List<Record>()
{
new Record(1, "Marc",
null),
new Record(2, "Ian",
1),
};
In the C# 2.0 section, I described the private setter for
properties. Let's look at the same implementation using auto-implemented
properties:
Hide Copy Code
public class AutoImplement
{
public int Readable
{ get; private set; }
public int Readable2
{ get { return 20; } }
public void Update()
{
// Allowed:
Readable = 10;
// Not allowed:
// Readable2 = 30;
}
}
The code is a lot cleaner, but the disadvantage is that, for
properties that need to fire events or have some other business logic or
validation associated with them, you have to go back to the old way of
implementing the backing field manually. One proposed solution to firing
property change events for auto-implemented properties is to use AOP
techniques, as written up by Tamir
Khason's Code Project technical blog.
Anonymous types lets us create "structures"
without defining a backing class or struct, and rely on implicit types (vars)
and object initializers. For example, if we have a collection of
"Record" objects, we can return a subset of the properties in this
LINQ statement:
Hide Copy Code
public void Example()
{
List<Record>
records = new List<Record>();
{
new Record(1, "Marc",
null),
new Record(2, "Ian",
1),
};
var idAndName = from
r in records select new { r.ID, r.Name };
}
Here we see how several features come into play at once:
LINQ
Implicit types
Object initialization
Anonymous types
If we run the debugger and inspect "idAndName",
we'll see that it has a value:
Hide Copy Code
{System.Linq.Enumerable.WhereSelectListIterator<CSharpComparison.Record,
<>f__AnonymousType0<int,string>>}
and (ready for it?) the type:
Hide Copy Code
System.Collections.Generic.IEnumerable<<>f__AnonymousType0<int,string>>
{System.Linq.Enumerable.WhereSelectListIterator<CSharpComparison.Record,
<>f__AnonymousType0<int,string>>}
Imagine having to explicitly state that type name. We can
see advantages of implicit types, especially in conjunction with anonymous
types.
Extension methods are a mechanism for extending the behavior
of a class external to its implementation. For example, the String class is
sealed, so we can't inherit from it, but there's a lot of useful functions that
the String class doesn't provide. For example, working with Graphviz, I often
need to put quotes around the object name.
Before extension methods, I would probably end up writing
something like this:
Hide Copy Code
string graphVizObjectName = "\"" + name +"\"";
Not very readable, re-usable, or bug proof (what if name is
null?)
With extension methods, I can write an extension:
Hide Copy Code
public static class StringHelpersExtensions
{
public static string
Quote(this String src)
{
return "\""
+ src + "\"";
}
}
(OK, that part looks pretty much the same) - but I would use
it like this:
Hide Copy Code
string graphVizObjectName = name.Quote();
Not only is this more readable, but it's also more reusable,
as the behavior is now exposed everywhere.
Query expressions seems to be a synonymous phrase for LINQ
(Language-Integrated Query). Humorously, the Microsoft website I just
referenced has the header "LINQ Query Expressions." Redundant!
Query expressions are written in a declarative syntax and
provide the ability to query an enumerable or "queriable" object
using complex filters, ordering, grouping, and joins, very similar in fact to
how you would work with SQL and relational data.
As I wrote about above with regards to anonymous types,
here's a LINQ statement:
Hide Copy Code
var idAndName = from r in records select new { r.ID, r.Name
};
LINQ expressions can get really complex and working with
.NET classes and LINQ relies heavily on extension methods. LINQ is far to large
a topic (there are whole books
on the subject) and is definitely outside the purview of this article!
Joins by default in LINQ are inner joins. I was perusing
recently for how to do left and right joins and came across this
useful post.
Lambda expressions are a fundamental part of working with
LINQ. You usually will not find LINQ without lambda expressions. A lambda
expression is an anonymous method (ah ha!) that "can contain expressions
and statements, and can be used to create delegates or expression tree
types...The left side of the lambda operator specifies the input parameters (if
any) and the right side holds the expression or statement block." (taken
from the website referenced above.)
In LINQ, I could write:
Hide Copy Code
var idAndName = from r in records
where r.Name=="Marc"
select new { r.ID,
r.Name };
and I'd get the names of people with the name
"Marc". With a lambda expression and the extension methods provided
for a generic List, I can write:
Hide Copy Code
var idAndName2 = records.All(r => r.Name == "Marc");
LINQ and lambda expressions can be combined. For example,
here's some code from an article I recently wrote:
Hide Copy Code
var unassoc = from et in dataSet.Tables["EntityType"].AsEnumerable()
where
!(dataSet.Tables["RelationshipType"].AsEnumerable().Any(
rt =>
(rt.Field<int>("EntityATypeID")
== assocToAllEntity.ID) &&
(rt.Field<int>("EntityBTypeID")
== et.Field<int>("ID"))))
select new { Name =
et.Field<string>("Name"), ID = et.Field<int>("ID")
};
LINQ, lambda expressions, anonymous types, implicit types,
collection initializers and object initializers all work together to more
concisely express the intent of the code. Previously, we would have to do this
with nested for loops and lots of "if" statements.
Let's revisit the MyVector example. With expression trees,
we can however compile type-specific code at runtime that allows us to work
with generic numeric types in a performance efficient manner (compare with
"dynamic" in C# 4.0, discussed below).
Hide Copy Code
public class MyVector<T>
{
private static readonly
Func<T, T, T> Add;
// Create and cache
adder delegate in the static constructor.
// Will throw a
TypeInitializationException if you can't add Ts or if T + T != T
static MyVector()
{
var firstOperand = Expression.Parameter(typeof(T),
"x");
var secondOperand
= Expression.Parameter(typeof(T), "y");
var body =
Expression.Add(firstOperand, secondOperand);
Add =
Expression.Lambda<Func<T, T, T>>(body, firstOperand, secondOperand).Compile();
}
public T X { get; set;
}
public T Y { get; set;
}
public MyVector(T x,
T y)
{
X = x;
Y = y;
}
public
MyVector<T> AddVector(MyVector<T> v)
{
return new
MyVector<T>(Add(X, v.X), Add(Y, v.Y));
}
}
The above example comes from a post on StackOverflow.
Let's revisit the MyVector implementation again. With the
dynamic keyword, we can defer the operation to runtime when we know the type.
Hide Copy Code
public class MyVector<T>
{
public MyVector() {}
public MyVector<T>
AddVector(MyVector<T> v)
{
return new
MyVector<T>()
{
X = (dynamic)X +
v.X,
Y = (dynamic)Y +
v.Y,
};
}
}
Because this uses method invocation and reflection, it is
very performance inefficient. According to MSDN referenced in the link above: The
dynamic type simplifies access to COM APIs such as the Office Automation APIs,
and also to dynamic APIs such as IronPython libraries, and to the HTML Document
Object Model (DOM).
As with the dynamic keyword, the primary purpose of this is
to facilitate calls to COM. From the MSDN link referenced above:
Named arguments enable you to specify an argument for a
particular parameter by associating the argument with the parameter's name
rather than with the parameter's position in the parameter list. Optional
arguments enable you to omit arguments for some parameters. Both techniques can
be used with methods, indexers, constructors, and delegates.
When you use named and optional arguments, the arguments are
evaluated in the order in which they appear in the argument list, not the
parameter list.
Named and optional parameters, when used together, enable
you to supply arguments for only a few parameters from a list of optional
parameters. This capability greatly facilitates calls to COM interfaces such as
the Microsoft Office Automation APIs.
I have never used named arguments and I rarely need to use
optional arguments, though I remember when I moved from C++ to C#, kicking and
screaming that optional arguments weren't part of the C# language
specification!
We can use named an optional arguments to specifically
indicate which arguments we are supplying to a method:
Hide Copy Code
public class NamedAndOptionalArgs
{
public void Foo()
{
Bar(a: 1, c: 5);
}
public void Bar(int
a, int b=1, int c=2)
{
// do something.
}
}
As this example illustrates, we can specify the value for a,
use the default value for b, and specify a non-default value for c. While I
find named arguments to be of limited use in regular C# programming, optional
arguments are definitely a nice thing to have.
Previously, we would have to write something like this:
Hide Copy Code
public void OldWay()
{
BarOld(1);
BarOld(1, 2);
}
public void BarOld(int a)
{
// 5 being the
default value.
BarOld(a, 5);
}
public void BarOld(int a, int b)
{
// do something.
}
The syntax available in C# 4.0 is much cleaner.
What do these words even mean? From Wikipedia:
covariant: converting from wider to smaller (like double to
float)
contravariant: converting from narrower to wider (like float
to double)
First, let's look at co-contravariance with delegates, which
has been around since Visual Studio 2005.
Not wanting to restate the excellent "read more"
example referenced above, I will simply state that covariance allows us to
assign a method returning a sub-class type to the delegate defined as returning
a base class type. This is an example of going from something wider (the base
class) to something smaller (the inherited class) in terms of derivation.
Contravariance, with regards to delegates, lets us create a
method in which the argument is the base class and the caller is using a sub-class
(going from narrower to wider). For example, I remember being annoyed that I
could not consume an event having a MouseEventArgs argument with a generic
event handler having an EventArgs argument. This example of contravariance has
been around since VS2005, but it makes for a useful example of the concept.
Again, the MSDN page referenced is an excellent read (in my
opinion) on co-contravariance with generics. To briefly summarize: as with
delegates, covariance allows a generic return type to be covariant, being able
specify a "wide" return type (more general) but able to use a
"smaller" (more specialized) return type. So, for example, the
generic interfaces for enumeration support covariance.
Conversely, contravariance lets us go from something narrow
(more specialized, a derived class) to something wider (more general, a base
class), and is used as parameters in generic interfaces such as IComparer.
To specify a covariant return parameter, we use the
"out" keyword in the generic type. To specify a contravariant method
parameter, we use the "in" keyword in the generic type. For example (read more
here):
Hide Copy Code
public delegate T2 MyFunc<in T1,out T2>(T1 t1);
T2 is the covariant return type and T1 is the
contravariant method parameter
Value Type
Value type
variables can be assigned a value directly. They are derived from the classSystem.ValueType.
The value
types directly contain data. Some examples are int,
char, and float, which stores numbers, alphabets, and floating point
numbers, respectively. When you declare an inttype, the system allocates memory to
store the value.
The following
table lists the available value types in C# 2010:
To get the
exact size of a type or a variable on a particular platform, you can use the sizeofmethod.
The expression sizeof(type) yields the storage size of the object
or type in bytes. Following is an example to get the size of int type on any machine:
using System; namespace DataTypeApplication { class Program { static void Main(string[] args) { Console.WriteLine("Size of int: {0}", sizeof(int)); Console.ReadLine(); } } }
When the
above code is compiled and executed, it produces the following result:
Size of int: 4
Reference Type
The
reference types do not contain the actual data stored in a variable, but they
contain a reference to the variables.
In other
words, they refer to a memory location. Using multiple variables, the reference
types can refer to a memory location. If the data in the memory location is
changed by one of the variables, the other variable automatically reflects this
change in value. Example ofbuilt-in reference types are: object, dynamic, and string.
Object Type
The Object
Type is the ultimate base
class for all data types in C# Common Type System (CTS). Object is an alias for
System.Object class. The object types can be assigned values of any other
types, value types, reference types, predefined or user-defined types. However,
before assigning values, it needs type conversion.
When a
value type is converted to object type, it is called boxing and on the other hand, when an object
type is converted to a value type, it is called unboxing.
object obj; obj = 100; // this is boxing
Dynamic Type
You can
store any type of value in the dynamic data type variable. Type checking for
these types of variables takes place at run-time.
Syntax for
declaring a dynamic type is:
dynamic <variable_name> = value;
For
example,
dynamic d = 20;
Dynamic
types are similar to object types except that type checking for object type
variables takes place at compile time, whereas that for the dynamic type
variables takes place at run time.
String Type
The String
Type allows you to assign any
string values to a variable. The string type is an alias for the System.String
class. It is derived from object type. The value for a string type can be
assigned using string literals in two forms: quoted and @quoted.
For
example,
String str = "Tutorials Point";
A @quoted
string literal looks as follows:
@"Tutorials Point";
The
user-defined reference types are: class, interface, or delegate. We will
discuss these types in later chapter.
Pointer Type
Pointer
type variables store the memory address of another type. Pointers in C# have
the same capabilities as the pointers in C or C++.
Syntax for
declaring a pointer type is:
type* identifier;
For
example,
char* cptr; int* iptr;
We will
discuss pointer types in the chapter 'Unsafe Codes'.
Boxing is the process of converting a value type to
the type object or to any interface type implemented by this value type. When
the CLR boxes a value type, it wraps the value inside a System.Object and
stores it on the managed heap. Unboxing extracts the value type from the
object. Boxing is implicit; unboxing is explicit. The concept of boxing and
unboxing underlies the C# unified view of the type system in which a value of
any type can be treated as an object.
In the
following example, the integer variable i is boxed and assigned to object o.
C#
int i = 123;
// The following line boxes i.
object o = i;
The object o can then be unboxed and
assigned to integer variable i:
C#
o = 123;
i = (int)o; // unboxing
Console
Application
The Console class contains the following methods for reading
console input and writing console output:
·
The
overloads of the ReadKey
method read an individual character.
·
The ReadLine
method reads an entire line of input.
·
The Write
method overloads convert an instance of a value type, an array of characters,
or a set of objects to a formatted or unformatted string, and then write that
string to the console.
·
A
parallel set of WriteLine
method overloads output the same string as the Write
overloads but also add a line termination string.
The Console class also contains methods and properties to
perform the following operations:
·
Get
or set the size of the screen buffer. The BufferHeight
and BufferWidth
properties let you get or set the buffer height and width, respectively, and
the SetBufferSize
method lets you set the buffer size in a single method call.
·
Get
or set the size of the console window. The WindowHeight
and WindowWidth
properties let you get or set the window height and width, respectively, and
the SetWindowSize
method lets you set the window size in a single method call.
·
Get
or set the size of the cursor. The CursorSize
property specifies the height of the cursor in a character cell.
·
Get
or set the position of the console window relative to the screen buffer. The WindowTop
and WindowLeft
properties let you get or set the top row and leftmost column of the screen
buffer that appears in the console window, and the SetWindowPosition
method lets you set these values in a single method call.
·
Get
or set the position of the cursor by getting or setting the CursorTop
and CursorLeft
properties, or set the position of the cursor by calling the SetCursorPosition
method.
·
Move
or clear data in the screen buffer by calling the MoveBufferArea
or Clear
method.
·
Get
or set the foreground and background colors by using the ForegroundColor
and BackgroundColor
properties, or reset the background and foreground to their default colors by
calling the ResetColor
method.
·
Play
the sound of a beep through the console speaker by calling the Beep
method.
Examples
|
Loop Type
|
Description
|
It
repeats a statement or a group of statements while a given condition is true.
It tests the condition before executing the loop body.
|
|
It
executes a sequence of statements multiple times and abbreviates the code
that manages the loop variable.
|
|
It
is similar to a while statement, except that it tests the condition at the
end of the loop body
|
|
You
can use one or more loop inside any another while, for or do..while loop.
|
Array
An array is a collection of same type variables
which can be accessed using numeric index. The numeric index is written in
square brackets after the array name.
Contents
·
Characteristics
·
Single Dimension Array
·
Iterating Through Single Dimension Array
·
2D Arrays
·
3D Arrays ...
·
Bounds of Multi Dimensional Arrays
·
Jagged Arrays
·
Mixed Arrays
Following is the declaration of a single dimension
array:
Hide Copy
Code
int[ ] roll = new int[8];

Characteristics
The numeric index is zero based. It goes from 0 to
n-1 where n is size of array (total number of elements in array).
On declaration, default value of numeric type
arrays is set to 0, and reference types are set to
null
.
Arrays are stored in continuous memory locations as
shown in the figure.
In C#, arrays are objects, and they have certain
properties like
Length
,
which can be used by using (.
) and
property name. All arrays are derived from abstract
class arrays so many built-in methods can be called.
Hide Copy
Code
//Rank propety Return number of dimensions
int[ ] single = new int[4] { 1, 2, 3, 4 };
int dimension = single.Rank;
Single Dimension Arrays
It is the simplest form of array, it's kind of a
row with n columns where each column is accessed with the zero based index. The
above two code examples show single dimension arrays.
In C#, declaration of array is a bit different from
C or C++. Here square brackets are placed after type name, then array-name,
keyword new and then type with square brackets containing size of array.
Hide Copy
Code
Type[ ] arrayname = new Type[size];
The following code initializes a single dimension
integer array of size 5. It contains 5 elements which can be accessed by
arr[0]
to arr[4]
.
Hide Copy
Code
//Integer Array declaration
int[ ] arr = new int[5];
Character type arrays are declared as follows:
Hide Copy
Code
//Character type array
char[ ] name = new char[10];

In the same way,
string
type arrays are declared:
Hide Copy
Code
//String array declaration
string[ ] days = new string[7];

There are many ways to assign values to array.
Array can be initialized in declaration line by placing the values between
curly braces { } in comma separated fashion. Characters are placed in single
quotes and
string
s are placed in
double quotes.
Hide Copy
Code
//Integer Array Initialization
int[ ] arr = new int[5] { 1, 2, 3, 4, 5 };
//Character Array Initialization
char[ ] name = new char[10] { 'i', ' ', 'a', 'm', ' ', 'f', 'i', 'n', 'e', '.' };
//String Array Initialization
string[ ] days = new string[7] { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
While initializing the array, size of array may be
omitted. Then size of array will be calculated number of elements written in
curly braces.
One other way of declaring, initializing array is:
Hide Copy
Code
//Integer Array Declaration, Initialization
int[ ] arr;arr = new int[5] { 1, 2, 3, 4, 5 };
Following way of assigning values to array will
cause Error.
Hide Copy
Code
//Wrong way of writing
int[ ] arr = new int[5];
arr = { 1, 2, 3, 4, 5 };
Iterating Through Single Dimension Array
Since in C#, arrays are objects and they retain
certain built in properties.
Length
property
returns total number of elements in array. Right now we are dealing with single
dimension, so total number of elements is equal to size of array.
Hide Copy
Code
for (int i = 0; i < arr.Length; i++)
{
Console.WriteLine(i);
}Console.ReadLine();

Multi Dimensional Arrays
2D Arrays
Arrays can be multidimensional. The most widely
used are two dimensional arrays, often Matrices form 2D arrays. In 2D array, 2
zero based index are used to access a particular value in the array.
Hide Copy
Code
//Integer 2D Array
int[,] matrix = new int[10, 10];
//Accessing Value
int val = matrix[5, 7];

Value of element stored in 5th Row, 7th
Column i.e., 58, will be assigned to variable
val
.
Rows and Columns have zero based index. Total number of values which can be
stored in 2D array is equal to product of rows and columns. For the above
case, it is 100.
Single dimension array is a single Row with columns
>0. 2D arrays have more than one Row, thus form a table.

Accessing the element stored in 3rd row,
4th column in balances table.
To initialize 2D array, each row values are placed
in curly braces as in the case of a single dimensional array and then
these set of curly braces for all rows are placed in another set of curly
braces in the same fashion.
Hide Copy
Code
//2D Array Initializtion
int[,] arr_2d = new int[3, 2] { { 1, 2 }, { 3, 4 }, { 5, 6 } };
//Initializing 2Dimensional Array
char[,] day = new char[2, 3] { { 'o', 'n', 'e' }, { 't', 'w', 'o' } };
In the above piece of code, there are 3 rows, 2
columns, thus total number of elements is 2 x 3 = 6. It's hard to initialize 2D
array shown in the 1st figure, where it has 10 rows and 10 columns.
Loops can be used to assign values to each location.
Hide Copy
Code
//Assigning Values to matrix[10,10] array
for (int i = 0; i < 10; i++)
{
for (int j = 0; j < 10; j++)
{
matrix[i, j] = i * 10 + j + 1;
}
}
In case of multidimensional arrays, knowing number of
dimensions is sometimes necessary to have more grip over the array. Rank
property returns number of dimensions of the array.
Hide Copy
Code
//Getting Number of dimensions of array
int dim = matrix.Rank;
The
GetUpperBound
function
returns the upper bound of the array in a particular dimension.
Hide Copy
Code
for (int i = 0; i <= matrix.GetUpperBound(0);i++)
{
for (int j = 0; j <= matrix.GetUpperBound(1); j++)
{
Console.Write(matrix[i, j].ToString() + "\t");
}
Console.WriteLine();
}
Output of the above piece of code is as follows:

GetLowerBound
method
gets the lower bound of the array in a particular dimension. The following
figure shows the difference between length, upper bound and lower bound.
3D Arrays
We can have more than two dimensions for arrays as well. For three dimensional array, we need three indexes to access each element in array. Example of 3 dimensional array can be a point in space. Consider a block of small bricks, as shown in figure below, to address each small brick, there is one index for row, one for column and one for depth.

Hide Copy
Code
//Block code 3D array
int[, ,] block_3d = new int[2, 3, 4];
4D Arrays
Example of 4 dimensional array can be taken as one second in a week, there are 60 seconds in one hour, 60 mins in one hour, 24 hours a day and 7 day a week.

Hide Copy
Code
//Week 4D array
int[, , ,] week = new int[7, 24, 60, 60];

Jagged Arrays
Array of arrays are called jagged arrays.

The statement might be confusing but consider the
example of saving marks of few students who are studying different number of
subjects.
Hide Copy
Code
Student-1 marks 65, 60, 76
Student-2 marks 78, 92, 68, 90, 55
Student-3 marks 45, 59, 88, 72
If we use 2 Dimensional array to store the above
marks, then an array of 3 rows and 5 columns is needed. The extra info needs to
be added at locations for which marks don't exist.
65
|
60
|
76
|
0
|
0
|
78
|
92
|
68
|
90
|
55
|
45
|
53
|
88
|
72
|
0
|
Jagged arrays come in handy in such situations.
Jagged arrays may have different sizes and dimensions. For this situation, we
need one single dimension array with three elements, and each of its elements
is a single dimension array with length 3, 5, 4 respectively.
Hide Copy
Code
//Jagged arrays
int[ ][ ] student = new int[3][ ];
In the above piece of code, two sets of square
brackets are used. Now each element of Jagged array needs to be assigned to a
single dimension array.
Hide Copy
Code
//Declaring Each Element of Jagged Array
student[0] = new int[3];
student[1] = new int[5];
student[2] = new int[4];
Values can also be assigned just like single
dimension array by placing after square brackets.
Hide Copy
Code
//Initializing Each Element of Jagged Array
student[0] = new int[3] { 65, 60, 76 };
student[1] = new int[5] { 78, 92, 68, 90, 55 };
student[2] = new int[4] { 45, 59, 88, 72 };
A short way of doing this is:
Hide Copy
Code
//Jagged arrays
int[ ][ ] student = new int[3][ ]
{new int[3] { 65, 60, 76 },new int[5] { 78, 92, 68, 90, 55 },
new int[4] { 45, 59, 88, 72 }};
Jagged arrays are reference type, thus they are
initialized to
null
. To
access elements in jagged array in student
example, 2 indexes are used.//Accessing elements in Jagged Array
student[2][2] = 80;for (int i = 0; i < student.Length; i++)
{
for (int j = 0; j < student[i].Length; j++)
{
Console.Write(student[i][j]);
Console.Write('\t');
}
Console.WriteLine();
}

Mixed Arrays
Combination of jagged and multidimensional arrays
is known as mixed arrays. In case of multidimensional arrays of different
sizes, mixed arrays are used. Consider there are three tables, each with
different number or rows and columns.
Hide Copy
Code
Table-1 3 rows, 5 columns
Table-2 4 rows, 3 columns
Table-2 6 rows, 4 columns
Hide Copy
Code
//Mixed Arrays int [ ][,]mixed=new int[3][ ]
{
new int[3,5],
new int[4,3],
new int[6,4]
};
C# Arrays
There are
following few important concepts related to array which should be clear to a C#
programmer:
Concept
|
Description
|
C#
supports multidimensional arrays. The simplest form of the multidimensional
array is the two-dimensional array.
|
|
C#
supports multidimensional arrays, which are arrays of arrays.
|
|
You
can pass to the function a pointer to an array by specifying the array's name
without an index.
|
|
This
is used for passing unknown number of parameters to a function.
|
|
Defined
in System namespace, it is the base class to all arrays, and provides various
properties and methods for working with arrays.
|
Functions
A function allows you to encapsulate a piece of code and call it
from other parts of your code. You may very soon run into a situation where you
need to repeat a piece of code, from multiple places, and this is where
functions come in. In C#, they are basically declared like this:
<visibility>
<return type> <name>(<parameters>)
{
<function code>
}
To call a function, you simply write its name, an open
parenthesis, then parameters, if any, and then a closing parenthesis, like
this:
DoStuff();
Here is an example of our DoStuff() function:
public
void DoStuff()
{
Console.WriteLine("I'm doing
something...");
}
The first part, public, is the visibility, and is optional. If
you don't define any, then the function will be private. More about that later
on.
Next is the type to return. It could be any valid type in C#, or as we have done it here, void. A void means that this function returns absolutely nothing. Also, this function takes no parameters, as you can see from the empty set of parentheses, so it's actually just a tad bit boring. Let's change that:
Next is the type to return. It could be any valid type in C#, or as we have done it here, void. A void means that this function returns absolutely nothing. Also, this function takes no parameters, as you can see from the empty set of parentheses, so it's actually just a tad bit boring. Let's change that:
public
int AddNumbers(int number1, int number2)
{
int result = number1 + number2;
return result;
}
We've changed almost everything. The function now returns an
integer, it takes two parameters (both integers), and instead of outputting
something, it makes a calculation and then returns the result. This means that
we can add two numbers from various places in our code, simply by calling this
function, instead of having to write the calculation code each time. While we
don't save that much time and effort in this small example, you better believe
that you will learn to love functions, the more you use C#. This function is
called like this:
int
result = AddNumbers(10, 5);
Console.WriteLine(result);
As
mentioned, this function actually returns something, and it has to, because we
told C# that it's supposed to do so. When declaring anything else than void as
a return type, we are forcing our self to return something. You can try
removing the return line from the example above, and see the compiler complain:
'AddNumbers(int, int)': not all code paths return a value
Structures
'AddNumbers(int, int)': not all code paths return a value
Structures
To define a structure, you
must use the struct statement. The struct statement defines a new data type,
with more than one member for your program.
For example, here is the
way you can declare the Book structure:
struct Books
{
public string title;
public string author;
public string subject;
public int book_id;
};
The following program shows
the use of the structure:
using System;
struct Books
{
public string title;
public string author;
public string subject;
public int book_id;
};
public class testStructure
{
public static void Main(string[] args)
{
Books Book1; /* Declare Book1 of type Book */
Books Book2; /* Declare Book2 of type Book */
/*
book 1 specification */
Book1.title = "C
Programming";
Book1.author = "Nuha Ali";
Book1.subject = "C Programming
Tutorial";
Book1.book_id = 6495407;
/*
book 2 specification */
Book2.title = "Telecom
Billing";
Book2.author = "Zara Ali";
Book2.subject = "Telecom Billing Tutorial";
Book2.book_id = 6495700;
/*
print Book1 info */
Console.WriteLine( "Book 1 title :
{0}", Book1.title);
Console.WriteLine("Book
1 author : {0}", Book1.author);
Console.WriteLine("Book
1 subject : {0}", Book1.subject);
Console.WriteLine("Book
1 book_id :{0}", Book1.book_id);
/*
print Book2 info */
Console.WriteLine("Book
2 title : {0}", Book2.title);
Console.WriteLine("Book
2 author : {0}", Book2.author);
Console.WriteLine("Book
2 subject : {0}", Book2.subject);
Console.WriteLine("Book
2 book_id : {0}", Book2.book_id);
Console.ReadKey();
}
}
When the above code is
compiled and executed, it produces the following result:
Book
1 title : C Programming
Book
1 author : Nuha Ali
Book
1 subject : C Programming Tutorial
Book
1 book_id : 6495407
Book
2 title : Telecom Billing
Book
2 author : Zara Ali
Book
2 subject : Telecom Billing Tutorial
Book
2 book_id : 6495700
No comments:
Post a Comment