Saturday, May 9, 2015
.NET Program Execution
.NET Program Execution
- Part 1
Let
us first understand how VB6 or C++ programs (Non Dotnet applications) used to
execute.
We know that computers only understand machine level code. Machine level code is also called as native or binary code. So, when we execute a VB6 or C++ program, the respective language compiler, compiles the respective language source code into native code, which can then be understood by the underlying operating system and hardware. This process is depicted in the image below.
Native code is specific (native) to the operating system on which it is generated. If you take this compiled native code and try to run on another operating system it will fail. So the problem with this style of program execution is that, it is not portable from one platform to another platform.
Let us now understand, how a .Net program executes. Using dotnet we can create different types of applications. A few of the common types of .NET applications include Web, Windows, Console and Mobile Applications. Irrespective of the type of the application, when you execute any .NET application the following happens
1. The .NET application gets compiled into Intermediate language (IL). IL is also referred as Common Intermediate language (CIL) and Microsoft Intermediate language (MSIL). Both .NET and non .NET applications generate an assembly. Assemblies have an extension of .DLL or .EXE. For example if you compile a windows or Console application, you get a .EXE, where as when we compile a web or Class library project we get a .DLL. The difference between a .NET and NON .NET assembly is that, DOTNET Assembly is in intermediate language format where as NON DOTNET assembly is in native code format.
2. NON DOTNET applications can run directly on top of the operating system, where as DOTNET applications run on top of a virtual environment called as Common Language Runtime (CLR). CLR contains a component called Just In-Time Compiler (JIT), which will convert the Intermediate language into native code which the underlying operating system can understand.
So, in .NET the application execution consists of 2 steps
1. Language compiler, compiles the Source Code into Intermediate Language (IL)
2. JIT compiler in CLR converts, the IL into native code which can then be run on the underlying operating system.
This process is shown in the image below.
Since, a .NET assembly is in Intermedaite Language format and not native code, .NET assemblies are portable to any platform, as long as the target platform has the Common Language Runtime (CLR). The target platform's CLR converts the Intermedaite Language into native code that the underlying operating system can understand. Intermediate Languge is also called as managed code. This is because CLR manages the code that runs inside it. For example, in a VB6 program, the developer is responsible for de-allocating the memory consumed by an object. If a programmer forgets to de-allocate memory, we may run into hard to detecct out of memory exceptions. On the other hand a .NET programmer need not worry about de-allocating the memory consumed by an object. Automatic memory management, also known as grabage collection is provided by CLR. Apart, from garbage collection, there are several other benefits provided by the CLR, which we will discuss in a later session. Since, CLR is managing and executing the Intermediate Language, it (IL) is also called as managed code.
.NET supports different programming languages like C#, VB, J#, and C++. C#, VB, and J# can only generate managed code (IL), where as C++ can generate both managed code (IL) and un-managed code (Native code).
The native code is not stored permanently anywhere, after we close the program the native code is thrown awaya. When we execute the program again, the native code gets generated again.
.NET program is similar to java program execution. In java we have byte codes and JVM (Java Virtual Machine), where as in .NET we Intermediate Language and CLR (Common Language Runtime)
We know that computers only understand machine level code. Machine level code is also called as native or binary code. So, when we execute a VB6 or C++ program, the respective language compiler, compiles the respective language source code into native code, which can then be understood by the underlying operating system and hardware. This process is depicted in the image below.
Native code is specific (native) to the operating system on which it is generated. If you take this compiled native code and try to run on another operating system it will fail. So the problem with this style of program execution is that, it is not portable from one platform to another platform.
Let us now understand, how a .Net program executes. Using dotnet we can create different types of applications. A few of the common types of .NET applications include Web, Windows, Console and Mobile Applications. Irrespective of the type of the application, when you execute any .NET application the following happens
1. The .NET application gets compiled into Intermediate language (IL). IL is also referred as Common Intermediate language (CIL) and Microsoft Intermediate language (MSIL). Both .NET and non .NET applications generate an assembly. Assemblies have an extension of .DLL or .EXE. For example if you compile a windows or Console application, you get a .EXE, where as when we compile a web or Class library project we get a .DLL. The difference between a .NET and NON .NET assembly is that, DOTNET Assembly is in intermediate language format where as NON DOTNET assembly is in native code format.
2. NON DOTNET applications can run directly on top of the operating system, where as DOTNET applications run on top of a virtual environment called as Common Language Runtime (CLR). CLR contains a component called Just In-Time Compiler (JIT), which will convert the Intermediate language into native code which the underlying operating system can understand.
So, in .NET the application execution consists of 2 steps
1. Language compiler, compiles the Source Code into Intermediate Language (IL)
2. JIT compiler in CLR converts, the IL into native code which can then be run on the underlying operating system.
This process is shown in the image below.
Since, a .NET assembly is in Intermedaite Language format and not native code, .NET assemblies are portable to any platform, as long as the target platform has the Common Language Runtime (CLR). The target platform's CLR converts the Intermedaite Language into native code that the underlying operating system can understand. Intermediate Languge is also called as managed code. This is because CLR manages the code that runs inside it. For example, in a VB6 program, the developer is responsible for de-allocating the memory consumed by an object. If a programmer forgets to de-allocate memory, we may run into hard to detecct out of memory exceptions. On the other hand a .NET programmer need not worry about de-allocating the memory consumed by an object. Automatic memory management, also known as grabage collection is provided by CLR. Apart, from garbage collection, there are several other benefits provided by the CLR, which we will discuss in a later session. Since, CLR is managing and executing the Intermediate Language, it (IL) is also called as managed code.
.NET supports different programming languages like C#, VB, J#, and C++. C#, VB, and J# can only generate managed code (IL), where as C++ can generate both managed code (IL) and un-managed code (Native code).
The native code is not stored permanently anywhere, after we close the program the native code is thrown awaya. When we execute the program again, the native code gets generated again.
.NET program is similar to java program execution. In java we have byte codes and JVM (Java Virtual Machine), where as in .NET we Intermediate Language and CLR (Common Language Runtime)
From Part 1 of DotNet basics videos, we
understood that, compiling any .NET application would produce an assembly.
Assemblies have an extension of .DLL or .EXE. For example if you compile a
windows or Console application, you get a .EXE, where as when we compile a web
or Class library project we get a .DLL. Please
watch Part 1, if you haven't done so already.
The entire source code of a project is compiled into Intermediate Language and packaged into the assembly. A .NET assembly consists of Manifest and Intermediate language. Manifest contains metadata about the assembly like the name, version, culture and strong name information. Metadata also contains information about the referenced assemblies. Each reference includes the dependent assembly's name, assembly metadata (version, culture, operating system, and so on), and public key, if the assembly is strong named.
Some information in the assembly manifest can be modified using attributes. For example to modify the version number follow these steps
1. Expand the properties folder in solution explorer. Every project in .NET has a properties folder.
2. Open AssemblyInfo.cs file that is present under properties folder.
3. In this file, you should see AssemblyVersion attribute, which is defaulted to 1.0.0.0. Change this to 2.0.0.0 and rebuild the solution.
4. Now open the assembly using ILDASM.exe
To peek inside an assembly with ILDASM follow these steps.
1. Navigate to Visual Studio Command Prompt (Start -> All Programs -> Microsoft Visual Studio 2010 -> Visual Studio Tools -> Right Click on Visual Studio Command Prompt 2012 and select "Run as Administrator")
2. Once you have the "Visual Studio Command Prompt 2012" open, type in the following command and press enter
Ildasm.exe C:\YourDirectoryPath\YourAssembly.exe
This command should open the assembly and you will find the manifest and the types (classes, structs etc..) in the assembly. At the bottom you can see the version of the assembly.
If you want to save the Intermediate Language to a text file.
1. Select File Menu from the ILDASM tool.
2. Select Dump and you will see "Dump Options Window"
3. Click OK on "Dump Options Window"
4. Now enter the file name of your choice. For this example let's enter sample and save it to the C: drive.
5. Now navigate to C: drive in windows explorer and you should see Sample.il
6. Open Sample.il with notepad and you should see assembly metadata and IL(Intermediate Language).
If you want to rebuild an Assembly from the Sample.il file we can use a tool ILASM.exe
1. Type the following command in "Visual Studio Command Prompt" and press enter
ILASM.exe C:\Sample.il
2. Now navigate to C: drive in windows explorer and you should see Sample.exe
We use ILDASM (Intermediate Language Disassembler) to peek at the assembly manifest and IL. You can also use this tool to export manifest and IL to a text file.
We use ILASM.exe (Intermediate Language Assembler) to reconstruct an assembly from a text file that contains manifest and IL
Strong naming an assembly or
Signing an assembly with strong name.
In .NET assemblies can be broadly classified into 2 types
1. Weak Named Assemblies
2. Strong Named Assemblies
An assembly name consists of 4 Parts
1. Simple textual name.
2. Version number.
3. Culture information (If provided, otherwise the assembly is language neutral)
4. Public key token
In .NET assemblies can be broadly classified into 2 types
1. Weak Named Assemblies
2. Strong Named Assemblies
An assembly name consists of 4 Parts
1. Simple textual name.
2. Version number.
3. Culture information (If provided, otherwise the assembly is language neutral)
4. Public key token
We use AssemblyVersion attribute to specify the Assembly version. The default is 1.0.0.0. The version number of an assembly consists of the following four parts:
1. Major Version
2. Minor Version
3. Build Number
4. Revision Number
You can specify all the values or you can default the Revision and Build Numbers by using the '*' as shown below:
[assembly: AssemblyVersion("2.1.*")]
AssemblyCulture attribute is used for specifying the culture. By default an assembly is language neutral, as the AssemblyCulture attribute contains empty string. If you specify any string other than an empty string for the culture parameter, the assembly becomes a satellite assembly. In fact, compilers use this attribute to distinguish between main assembly (language neutral) and a satellite assembly. We will talk about satellite assemblies in a later session.
We use AssemblyKeyFile attribute to sign the assembly with a strong name. To the constructor of AssemblyKeyFile attribute, we need to pass the path of the key file, that contains the private and public key. To generate the key file
1. Open Visual Studio Command Prompt
2. Type the command and press enter: sn.exe -k c:\KeyFile.snk
The key file with name KeyFile.snk should be generated in the C: drive. In SN.exe, SN stands for Strong Name. Key files have the extension of .snk
Finally, In AssemblyInfo.cs file of the project, specify AssemblyKeyFile attribute as shown below and build the project. This process will strongly name an assembly.
[assembly: AssemblyKeyFile("KeyFile.snk")]
A strongly named assembly should have all of the following
1. The textual assembly name.
2. The assembly Version number.
3. The assembly should have been signed with private/public key pair.
If the assembly is not signed with private/public key pair, the assembly is weak named and not guaranteed to be unique, and may cause DLL hell. Strong named assemblies are guaranteed to be unique and solves DLL hell. You cannot install an assembly into GAC unless, the assembly is strongly named.
In the upcoming video sessions we will discuss
1. What is GAC. How and when to install an assembly into GAC?
2. What is DLL HELL?
3. How is DLL HELL solved with .NET?
GAC stands for Global
Assembly Cache and contains strong named assemblies. Assemblies in the GAC
can be shared by all applications running on that machine, without having to
copy the assembly locally. It is recommended to install an assembly into GAC,
only when required and shared by applications, otherwise they should be kept
private. You shouldn't add an assembly into the GAC, if
you wish to deploy your application to another machine using XCopy deployment. This is because in XCopy deployment, we only copy the
application files to the target machine and not the GAC contents. XCopy
deployment is simply copying files from one location to another.
With the introduction of .NET 4.0, we have 2 GAC's. One for DotNet 2.0 to 3.5 assemblies and the other for .NET 4.0 assemblies. The following are the paths for the 2 GAC's
1. C:\Windows\Assembly - For .NET 2.0 - 3.5 assemblies
2. C:\WINDOWS\Microsoft.NET\assembly - For .NET 4.0 assemblies
To install an assembly into the GAC, the assembly must be strongly named, otherwise you get an error stating - Failure adding assembly to the cache: Attempt to install an assembly without a strong name. There are 2 ways to install an assembly into GAC.
1. Simply Drag and Drop
2. Use GacUtil.exe (GAC Utility Tool)
To install an assembly using gacutil, use the following command. This command installs SampleAssembly.dll into the GAC. If you have build this project using .NET framwork 4.0 then look in C:\WINDOWS\Microsoft.NET\assembly, else look in C:\Windows\Assembly.
Gacutil -i C:\SampleProject\SampleAssembly.dll
Note: If you are using Visual Studio 2010, then by default the target framework for any new project is .NET 4.0. If you want to change the target framework, right click the project and select properties. In the properties window, you can change the target framework version.
To uninstall an assembly from the GAC, using GAC utility, use the following command.
Gacutil -u MyClassLibrary
If there are multiple versions of MyClassLibrary assembly, in the GAC, then all these versions will be removed by the above command. If you want to remove only one of the assemblies then specify the full name as shown below.
gacutil -u ClassLibrary,Version=1.0.0.0,PublicKeyToken=eeaabf36d7783129
Note: Please make sure there are no spaces between Comma(,) and the words "Version" and PublicKeyToken, otherwise you get an error stating Unknown option: Version=1.0.0.0. Also, don't specify the assembly extension (.dll or .exe) when uninstalling, otherwise the assembly will not be uninstalled. You will just get a message stating Number of assemblies uninstalled = 0
How .NET finds the
assemblies during program execution - Part 5
1. .NET figures out what version is needed : Usually the information about the dependant assemblies is present in the application's assembly manifest. CLR checks the application configuration file, publisher policy file(if exists), and machine config file for information that overrides the version information stored in the calling assembly's manifest.
2. .NET searches GAC (Global Assembly Cache) : .NET searches GAC only if the assembly is strongly named.
3. If the assembly is not found in the GAC, and if there is a .config file, then .NET searches the location in the cofiguration file, else .NET searches directory containing the executable (.EXE)
4. If the assembly is not found, the application terminates with error.
What is DLL HELL in .NET
Let us try and understand DLL HELL problem with an example. Please
refer to the image below. If you want to know how dll hell
problem is solved in .net, you can read this article.
1. I have 2 applications, A1 and A2 installed on my computer.
2. Both of these applications use shared assembly shared.dll
3. Now, I have a latest version of Application - A2 available on the internet.
4. I download the latest version of A2 and install it on my machine.
5. This new installation has over written Shared.dll, which is also used by Application - A1.
6. Application - A2 works fine, but A1 fails to work, because the newly installed Shared.dll is not backward compatible.
So, DLL HELL is a problem where one application will install a new version of the shared component that is not backward compatible with the version already on the machine, causing all the other existing applications that rely on the shared component to break. With .NET versioning we donot have DLL HELL problem any more.
How is the DLL HELL problem solved in .NET
Please read What is dll hell? interview question, before proceeding with this article.
In short, the dll hell problem is solved in .NET by signing the shared assemblies with strong name. Please follow this article, to understand the process of strong naming an assembly.
In dot net all the shared assemblies are usually in the GAC. GAC stands for Global Assembly Cache. The path for GAC is C:\[OperatingSystemDirectory]\assembly. For example on my computer the path is C:\WINDOWS\assembly. The image below shows the shared assemblies in the GAC.
Only strong named assemblies can be copied into GAC. Strong named assemblies in .NET has 4 pieces in its name as listed below.
1. Simple Textual Name
2. Version Number
3. Culture
4. Public Key Token
All these four pieces put together, is called as the fully qualified name of the assembly. In the GAC image above Accessibility assembly has a version of 2.0.0.0.
Now consider the example below:
1. I have 2 applications, Application - A1 and Application - A2 which relies on the shared assembly Accessibility.dll (Version 2.0.0.0) as shown in the image below.
2. Now, I have a latest version of Application - A2 available on the internet.
3. I download the latest version of A2 and install it on my machine.
4. This new installation copies a newer version of Accessibility.dll into the GAC with version 3.0.0.0.
5. So, in the GAC we now have 2 versions of Accessibility.dll.
6. Application - A1 continues to use Accessibility.dll (version 2.0.0.0) and Application - A2 uses Accessibility.dll (version 3.0.0.0)
7. So, now the assemblies are able to reside side by side in the GAC. For this reason dot net assemblies are also said to be supporting side by side execution
In this session
1. We will learn the basic structure of a c# program. The program we used in this video is shown below.
// Namespace Declaration
using System;
class Pragim
{
public static void Main()
{
// Write to console
Console.WriteLine ("Welcome to PRAGIM Technologies!");
}
}
2. Understand the purpose of using System declaration - The namespace declaration, using System, indicates that you are using the System namespace. If you omit the using System, declaration, then you have to use the fully qualified name of the Console class. A namespace is used to organize your code and is collection of classes, interfaces, structs, enums and delegates. We will discuss about namespaces in detail in a later session.
3. Purpose of Main() method - Main method is the entry point into your application.
1. We will learn the basic structure of a c# program. The program we used in this video is shown below.
// Namespace Declaration
using System;
class Pragim
{
public static void Main()
{
// Write to console
Console.WriteLine ("Welcome to PRAGIM Technologies!");
}
}
2. Understand the purpose of using System declaration - The namespace declaration, using System, indicates that you are using the System namespace. If you omit the using System, declaration, then you have to use the fully qualified name of the Console class. A namespace is used to organize your code and is collection of classes, interfaces, structs, enums and delegates. We will discuss about namespaces in detail in a later session.
3. Purpose of Main() method - Main method is the entry point into your application.
Suggested Videos
In this video, we will discuss
1. Reading from the console
2. Writing to the console
3. Two ways to write to console
a) Concatenation
b) Place holder syntax – Most preferred
Code samples used in the demo
using System;
class Program
{
static void Main()
{
// Prompt the user for his name
Console.WriteLine("Please enter your name");
// Read the name from console
string UserName = Console.ReadLine();
// Concatenate name with hello word and print
Console.WriteLine("Hello " + UserName);
//Placeholder syntax to print name with hello word
//Console.WriteLine("Hello {0}", UserName);
}
}
Please note that C# is case sensitive language.
Suggested Videos
Part 1 - Introduction to C#
Part 2 - Reading and writing to console
In this video, we will discuss the different built-in types that are available in c#.
Built-in types in C#
1. Boolean type – Only true or false
2. Integral Types - sbyte, byte, short, ushort, int, uint, long, ulong, char
3. Floating Types – float and double
4. Decimal Types
5. String Type
Escape Sequences in C#
http://msdn.microsoft.com/en-us/library/h21280bw.aspx
Verbatim Literal is a string with an @ symbol prefix, as in @“Hello".
Verbatim literals make escape sequences translate as normal printable characters to enhance readability.
Practical Example:
Without Verbatim Literal : “C:\\Pragim\\DotNet\\Training\\Csharp” – Less Readable
With Verbatim Literal : @“C:\Pragim\DotNet\Training\Csharp” – Better Readable
Part 1 - Introduction to C#
Part 2 - Reading and writing to console
In this video, we will discuss the different built-in types that are available in c#.
Built-in types in C#
1. Boolean type – Only true or false
2. Integral Types - sbyte, byte, short, ushort, int, uint, long, ulong, char
3. Floating Types – float and double
4. Decimal Types
5. String Type
Escape Sequences in C#
http://msdn.microsoft.com/en-us/library/h21280bw.aspx
Verbatim Literal is a string with an @ symbol prefix, as in @“Hello".
Verbatim literals make escape sequences translate as normal printable characters to enhance readability.
Practical Example:
Without Verbatim Literal : “C:\\Pragim\\DotNet\\Training\\Csharp” – Less Readable
With Verbatim Literal : @“C:\Pragim\DotNet\Training\Csharp” – Better Readable
Part
5 - C# Tutorial - Common Operators
Suggested
Videos
Part 2 - Reading and writing to console
Part 3 - Built-in types
Part 4 - String type
In this video, we will discuss the common operators that are available in c# programming language.
Assignment Operator =
Arithmetic Operators like +,-,*,/,%
Comparison Operators like ==, !=,>, >=, <, <=
Conditional Operators like &&, ||
Ternary Operator ?:
Null Coalescing Operator ??
Program without ternary Operator
using System;
class Program
{
static void Main()
{
int Number = 10;
bool IsNumber10;
if (Number == 10)
{
IsNumber10 = true;
}
else
{
IsNumber10 = false;
}
Console.WriteLine("i == 10 is {0}", IsNumber10);
}
}
Same program with ternary Operator
using System;
class Program
{
static void Main()
{
int Number = 10;
bool IsNumber10 = Number == 10 ? true : false;
Console.WriteLine("i == 10 is {0}", IsNumber10);
}
}
Part 2 - Reading and writing to console
Part 3 - Built-in types
Part 4 - String type
In this video, we will discuss the common operators that are available in c# programming language.
Assignment Operator =
Arithmetic Operators like +,-,*,/,%
Comparison Operators like ==, !=,>, >=, <, <=
Conditional Operators like &&, ||
Ternary Operator ?:
Null Coalescing Operator ??
Program without ternary Operator
using System;
class Program
{
static void Main()
{
int Number = 10;
bool IsNumber10;
if (Number == 10)
{
IsNumber10 = true;
}
else
{
IsNumber10 = false;
}
Console.WriteLine("i == 10 is {0}", IsNumber10);
}
}
Same program with ternary Operator
using System;
class Program
{
static void Main()
{
int Number = 10;
bool IsNumber10 = Number == 10 ? true : false;
Console.WriteLine("i == 10 is {0}", IsNumber10);
}
}
Part 6 - C# Tutorial - Nullable Types
Suggested
Videos
Part 3 - Built-in types
Part 4 - String type
Part 5 - Operators
In this video, we will discuss
1. Nullable types in C#
2. Null Coalescing Operator ??
In C# types are divided into 2 broad categories.
Value Types - int, float, double, structs, enums etc
Reference Types – Interface, Class, delegates, arrays etc
By default value types are non nullable. To make them nullable use ?
int i = 0 (i is non nullable, so "i" cannot be set to null, i = null will generate compiler error)
int? j = 0 (j is nullable int, so j=null is legal)
Nullable types bridge the differences between C# types and Database types
Program without using NULL coalescing operator
using System;
class Program
{
static void Main()
{
int AvailableTickets;
int? TicketsOnSale = null;
if (TicketsOnSale == null)
{
AvailableTickets = 0;
}
else
{
AvailableTickets = (int)TicketsOnSale;
}
Console.WriteLine("Available Tickets={0}", AvailableTickets);
}
}
The above program is re-written using NULL coalescing operator
using System;
class Program
{
static void Main()
{
int AvailableTickets;
int? TicketsOnSale = null;
//Using null coalesce operator ??
AvailableTickets = TicketsOnSale ?? 0;
Console.WriteLine("Available Tickets={0}", AvailableTickets);
}
}
Part 3 - Built-in types
Part 4 - String type
Part 5 - Operators
In this video, we will discuss
1. Nullable types in C#
2. Null Coalescing Operator ??
In C# types are divided into 2 broad categories.
Value Types - int, float, double, structs, enums etc
Reference Types – Interface, Class, delegates, arrays etc
By default value types are non nullable. To make them nullable use ?
int i = 0 (i is non nullable, so "i" cannot be set to null, i = null will generate compiler error)
int? j = 0 (j is nullable int, so j=null is legal)
Nullable types bridge the differences between C# types and Database types
Program without using NULL coalescing operator
using System;
class Program
{
static void Main()
{
int AvailableTickets;
int? TicketsOnSale = null;
if (TicketsOnSale == null)
{
AvailableTickets = 0;
}
else
{
AvailableTickets = (int)TicketsOnSale;
}
Console.WriteLine("Available Tickets={0}", AvailableTickets);
}
}
The above program is re-written using NULL coalescing operator
using System;
class Program
{
static void Main()
{
int AvailableTickets;
int? TicketsOnSale = null;
//Using null coalesce operator ??
AvailableTickets = TicketsOnSale ?? 0;
Console.WriteLine("Available Tickets={0}", AvailableTickets);
}
}
Part 7 - C# Tutorial - Datatype conversions
Suggested
Videos
Part 4 - String type
Part 5 - Operators
Part 6 - Nullable Types
In this video, we will discuss
1. Implicit conversions
2. Explicit Conversions
3. Difference between Parse() and TryParse()
Implicit conversion is done by the compiler:
1. When there is no loss of information if the conversion is done
2. If there is no possibility of throwing exceptions during the conversion
Example: Converting an int to a float will not loose any data and no exception will be thrown, hence an implicit conversion can be done.
Where as when converting a float to an int, we loose the fractional part and also a possibility of overflow exception. Hence, in this case an explicit conversion is required. For explicit conversion we can use cast operator or the convert class in c#.
Implicit Conversion Example
using System;
class Program
{
public static void Main()
{
int i = 100;
// float is bigger datatype than int. So, no loss of
// data and exceptions. Hence implicit conversion
float f = i;
Console.WriteLine(f);
}
}
Explicit Conversion Example
using System;
class Program
{
public static void Main()
{
float f = 100.25F;
// Cannot implicitly convert float to int.
// Fractional part will be lost. Float is a
// bigger datatype than int, so there is
// also a possiblity of overflow exception
// int i = f;
// Use explicit conversion using cast () operator
int i = (int)f;
// OR use Convert class
// int i = Convert.ToInt32(f);
Console.WriteLine(i);
}
}
Difference between Parse and TryParse
1. If the number is in a string format you have 2 options - Parse() and TryParse()
2. Parse() method throws an exception if it cannot parse the value, whereas TryParse() returns a bool indicating whether it succeeded or failed.
3. Use Parse() if you are sure the value will be valid, otherwise use TryParse()
Part 4 - String type
Part 5 - Operators
Part 6 - Nullable Types
In this video, we will discuss
1. Implicit conversions
2. Explicit Conversions
3. Difference between Parse() and TryParse()
Implicit conversion is done by the compiler:
1. When there is no loss of information if the conversion is done
2. If there is no possibility of throwing exceptions during the conversion
Example: Converting an int to a float will not loose any data and no exception will be thrown, hence an implicit conversion can be done.
Where as when converting a float to an int, we loose the fractional part and also a possibility of overflow exception. Hence, in this case an explicit conversion is required. For explicit conversion we can use cast operator or the convert class in c#.
Implicit Conversion Example
using System;
class Program
{
public static void Main()
{
int i = 100;
// float is bigger datatype than int. So, no loss of
// data and exceptions. Hence implicit conversion
float f = i;
Console.WriteLine(f);
}
}
Explicit Conversion Example
using System;
class Program
{
public static void Main()
{
float f = 100.25F;
// Cannot implicitly convert float to int.
// Fractional part will be lost. Float is a
// bigger datatype than int, so there is
// also a possiblity of overflow exception
// int i = f;
// Use explicit conversion using cast () operator
int i = (int)f;
// OR use Convert class
// int i = Convert.ToInt32(f);
Console.WriteLine(i);
}
}
Difference between Parse and TryParse
1. If the number is in a string format you have 2 options - Parse() and TryParse()
2. Parse() method throws an exception if it cannot parse the value, whereas TryParse() returns a bool indicating whether it succeeded or failed.
3. Use Parse() if you are sure the value will be valid, otherwise use TryParse()
Part
8 - C# Tutorial - Arrays in C#
Suggested
Videos
Part 5 - Operators
Part 6 - Nullable Types
Part 7 - Datatype Conversions
In this video, we will discuss
1. Arrays
2. Advantages and dis-advantages of arrays
An array is a collection of similar data types.
using System;
class Program
{
public static void Main()
{
// Initialize and assign values in different lines
int[] EvenNumbers = new int[3];
EvenNumbers[0] = 0;
EvenNumbers[1] = 2;
EvenNumbers[2] = 4;
// Initialize and assign values in the same line
int[] OddNumbers = { 1, 3, 5};
Console.WriteLine("Printing EVEN Numbers");
// Retrieve and print even numbers from the array
for (int i = 0; i < EvenNumbers.Length; i++)
{
Console.WriteLine(EvenNumbers[i]);
}
Console.WriteLine("Printing ODD Numbers");
// Retrieve and print odd numbers from the array
for (int i = 0; i < OddNumbers.Length; i++)
{
Console.WriteLine(OddNumbers[i]);
}
}
}
Advantages: Arrays are strongly typed.
Disadvantages: Arrays cannot grow in size once initialized. Have to rely on integral indices to store or retrieve items from the array.
Part 5 - Operators
Part 6 - Nullable Types
Part 7 - Datatype Conversions
In this video, we will discuss
1. Arrays
2. Advantages and dis-advantages of arrays
An array is a collection of similar data types.
using System;
class Program
{
public static void Main()
{
// Initialize and assign values in different lines
int[] EvenNumbers = new int[3];
EvenNumbers[0] = 0;
EvenNumbers[1] = 2;
EvenNumbers[2] = 4;
// Initialize and assign values in the same line
int[] OddNumbers = { 1, 3, 5};
Console.WriteLine("Printing EVEN Numbers");
// Retrieve and print even numbers from the array
for (int i = 0; i < EvenNumbers.Length; i++)
{
Console.WriteLine(EvenNumbers[i]);
}
Console.WriteLine("Printing ODD Numbers");
// Retrieve and print odd numbers from the array
for (int i = 0; i < OddNumbers.Length; i++)
{
Console.WriteLine(OddNumbers[i]);
}
}
}
Advantages: Arrays are strongly typed.
Disadvantages: Arrays cannot grow in size once initialized. Have to rely on integral indices to store or retrieve items from the array.
Part
9 - C# Tutorial - Comments
Suggested
Videos
Part 6 - Nullable Types
Part 7 - Datatype Conversions
Part 8 - Arrays
In this video, we will discuss
1. Single line comments
2. Multi line comments
3. Introduction to XML documentation comments
Single line Comments - //
Multi line Comments - /* */
XML Documentation Comments - ///
Comments are used to document what the program does and what specific blocks or lines of code do. C# compiler ignores comments.
To Comment and Uncomment, there are 2 ways
1. Use the designer
2. Keyboard Shortcut: Ctrl+K, Ctrl+C and Ctrl+K, Ctrl+U
Note: Don't try to comment every line of code. Use comments only for blocks or lines of code that are difficult to understand
Part 6 - Nullable Types
Part 7 - Datatype Conversions
Part 8 - Arrays
In this video, we will discuss
1. Single line comments
2. Multi line comments
3. Introduction to XML documentation comments
Single line Comments - //
Multi line Comments - /* */
XML Documentation Comments - ///
Comments are used to document what the program does and what specific blocks or lines of code do. C# compiler ignores comments.
To Comment and Uncomment, there are 2 ways
1. Use the designer
2. Keyboard Shortcut: Ctrl+K, Ctrl+C and Ctrl+K, Ctrl+U
Note: Don't try to comment every line of code. Use comments only for blocks or lines of code that are difficult to understand
A delegate
is a type safe function pointer.That is, they hold reference(Pointer) to a
function.
The signature of the delegate must match the signature of the function, the delegate points to, otherwise you get a compiler error. This is the reason delegates are called as type safe function pointers.
A Delegate is similar to a class. You can create an instance of it, and when you do so, you pass in the function name as a parameter to the delegate constructor, and it is to this function the delegate will point to.
Tip to remember delegate syntax: Delegates syntax look very much similar to a method with a delegate keyword.
Sample Delegate Program:
using System;
// Delegate Declaration.
public delegate void HelloFunctionDelegate(string Message);
class Pragim
{
public static void Main()
{
// Create the instance of the delegate and pass in the function
// name as the parameter to the constructor. The passed in
// function signature must match the signature of the delegate
HelloFunctionDelegate del = new HelloFunctionDelegate(Hello);
// Invoke the delegate, which will invoke the method
del("Hello from Delegte");
}
public static void Hello(string strMessge)
{
Console.WriteLine(strMessge);
}
}
The signature of the delegate must match the signature of the function, the delegate points to, otherwise you get a compiler error. This is the reason delegates are called as type safe function pointers.
A Delegate is similar to a class. You can create an instance of it, and when you do so, you pass in the function name as a parameter to the delegate constructor, and it is to this function the delegate will point to.
Tip to remember delegate syntax: Delegates syntax look very much similar to a method with a delegate keyword.
Sample Delegate Program:
using System;
// Delegate Declaration.
public delegate void HelloFunctionDelegate(string Message);
class Pragim
{
public static void Main()
{
// Create the instance of the delegate and pass in the function
// name as the parameter to the constructor. The passed in
// function signature must match the signature of the delegate
HelloFunctionDelegate del = new HelloFunctionDelegate(Hello);
// Invoke the delegate, which will invoke the method
del("Hello from Delegte");
}
public static void Hello(string strMessge)
{
Console.WriteLine(strMessge);
}
}
What is an exception?
An exception is an unforeseen error that occurs when a program is running.
Examples:
Trying to read from a file that does not exist, throws FileNotFoundException.
Trying to read from a database table that does not exist, throws a SqlException.
Program without exception handling
using System;
using System.IO;
class ExceptionHandling
{
public static void Main()
{
//This line will throw FileNotFoundException
StreamReader streamReader = new StreamReader("C:\\NonExistingFile.txt");
Console.WriteLine(streamReader.ReadToEnd());
//Closes the underlying stream and releases the system resources.
//If there is an exception before this line, the below line will never
//be executed and the resources are not relased
streamReader.Close();
}
}
An exception is an unforeseen error that occurs when a program is running.
Examples:
Trying to read from a file that does not exist, throws FileNotFoundException.
Trying to read from a database table that does not exist, throws a SqlException.
Program without exception handling
using System;
using System.IO;
class ExceptionHandling
{
public static void Main()
{
//This line will throw FileNotFoundException
StreamReader streamReader = new StreamReader("C:\\NonExistingFile.txt");
Console.WriteLine(streamReader.ReadToEnd());
//Closes the underlying stream and releases the system resources.
//If there is an exception before this line, the below line will never
//be executed and the resources are not relased
streamReader.Close();
}
}
Part 40 - C# Tutorial - Exception Handling
Showing actual unhandled exceptions to the end user is bad for two reasons
1. Users will be annoyed as they are cryptic and does not make much sense to
the end users.
2. Exceptions contain information, that can be used for hacking into your
application
using System;
using System.IO;
class ExceptionHandling
{
public static void Main()
{
StreamReader streamReader = null;
try
{
// This line will throw
FileNotFoundException
streamReader = new StreamReader("C:\\NonExistingFile.txt");
Console.WriteLine(streamReader.ReadToEnd());
}
// This catch block handles only
FileNotFoundException
catch (FileNotFoundException fileNotFoundException)
{
// Log or email the exception
// Code to log or
email exception details
// Display meaningful error
message to the end user
Console.WriteLine("Please
check if the file \"{0}\" is present",
fileNotFoundException.FileName);
}
// This catch block handles all the other
exceptions
catch (Exception exception)
{
Console.WriteLine(exception.Message);
}
finally
{
if (streamReader != null)
{
streamReader.Close();
}
}
}
}
An exception is actually a class that derives from System.Exception class. The
System.Exception class has several useful properties, that provide valuable
information about the exception.
Message: Gets a message that describes the current exception
Stack Trace: Provides the call stack to the line number in the method where the
exception occurred.
We use try, catch and finally blocks for exception handling.
try - The code that can possibly cause an exception will be in the try block.
catch - Handles the exception.
finally - Clean and free resources that the class was holding onto during the
program execution.
Specific exceptions will be caught before the base general exception, so
specific exception blocks should always be on top of the base exception block.
Otherwise, you will encounter a compiler error.
Note: It is a good practice to always release resources in the finally
block, because finally block is guarenteed to execute, irrespective of whether
there is an exception or not.
Bad way of cleaning resources.
using System;
using System.IO;
class ExceptionHandling
{
public static void Main()
{
StreamReader streamReader = null;
try
{
streamReader = new StreamReader("C:\\NonExistingFile.txt");
Console.WriteLine(streamReader.ReadToEnd());
}
catch (Exception)
{
throw new Exception("Intentional
Exception");
}
//This code will never be executed, hence
it is always a good
//practice to release resources in
the finally block
streamReader.Close();
}
}Part 41 - C# Tutorial - Inner Exceptions
The InnerException property returns the
Exception instance that caused the current exception.
To look at the inner exception, you have to make this program cuase an exception fail. To do that you have 3 options
1. Enter a Character instead of a number (Causes Format Exception)
2. Or Enter a very big number that an interger cannot hold (Causes Over Flow Exception)
3. Or Enter Zero for Second Number (Causes Divide By Zero Exception)
To look at the inner exception, you have to make this program cuase an exception fail. To do that you have 3 options
1. Enter a Character instead of a number (Causes Format Exception)
2. Or Enter a very big number that an interger cannot hold (Causes Over Flow Exception)
3. Or Enter Zero for Second Number (Causes Divide By Zero Exception)
Part 41 - C# Tutorial - Inner Exceptions
using System;
using System.IO;
class ExceptionHandling
{
public static void Main()
{
try
{
try
{
Console.WriteLine("Enter First Number");
int FN = Convert.ToInt32(Console.ReadLine());
Console.WriteLine("Enter Second Number");
int SN = Convert.ToInt32(Console.ReadLine());
int Result = FN / SN;
Console.WriteLine("Result = {0}", Result);
}
catch (Exception ex)
{
string filePath = @"C:\Sample Files\Log.txt";
if (File.Exists(filePath))
{
StreamWriter sw = new StreamWriter(filePath);
sw.Write(ex.GetType().Name + ex.Message + ex.StackTrace);
sw.Close();
Console.WriteLine("There is a problem! Plese try later");
}
else
{
//To retain the original exception pass it as a parameter
//to the constructor, of the current exception
throw new FileNotFoundException(filePath + " Does not Exist", ex);
}
}
}
catch (Exception ex)
{
//ex.Message will give the current exception message
Console.WriteLine("Current or Outer Exception = " + ex.Message);
//Check if inner exception is not null before accessing Message property
//else, you may get Null Reference Excception
if(ex.InnerException != null)
{
Console.WriteLine("Inner Exception = ", ex.InnerException.Message);
}
}
}
}
Sql server, .net and c# video tutorial
Free C#, .Net and Sql server video tutorial for beginners and
intermediate programmers.
Part 42 - C# Tutorial - Custom Exceptions
In
this part we will learn
1. When to create custom exceptions
2. Creating a custom exception from the scratch
3. Throwing and catching the custom exception
To understand custom exceptions, you should have good understanding of
Part 21 - Inheritance
Part 40 - Exception Handling Basics
Part 41 - Inner Exceptions
1. When to create custom exceptions
2. Creating a custom exception from the scratch
3. Throwing and catching the custom exception
To understand custom exceptions, you should have good understanding of
Part 21 - Inheritance
Part 40 - Exception Handling Basics
Part 41 - Inner Exceptions
Part
42 - C# Tutorial - Custom Exceptions
When do you usually go for creating your own custom exceptions?
If none of the already existing dotnet exceptions adequately describes the problem.
Consider that
1. I have an asp.net web application.
2. The application should allow the user to have only one logged in session.
3. If the user is already logged in, and if he opens another browser window and tries to login again, the application should throw an error stating he is already logged in another browser window.
With in the .NET framework we donot have any exception, that adequately describes this problem. So this scenario is one of the examples where you want to create a custom exception.
We know that an exception is a class. So to create a Custom exception,
1. Create a class that derives from System.Exception class. As a convention, end the class name with Exception suffix. All .NET exceptions end with, exception suffix. If you don't do so, you won't get a compiler error, but you will be deviating from the guidelines for creating custom exceptions.
public class UserAlreadyLoggedInException : Exception
{
}
2. Provide a public constructor, that takes in a string parameter. This constructor simply passes the string parameter, to the base exception class constructor.
public UserAlreadyLoggedInException(string message)
: base(message)
{
}
3. Using InnerExceptions, you can also track back the original exception. If you want to provide this capability for your custom exception class, then overload the constructor as shown below. If you are new to Constructor Overloading, please watch this video.
public UserAlreadyLoggedInException(string message, Exception innerException)
: base(message, innerException)
{
}
4. If you want your Exception class object to work across application domains, then the object must be serializable. To make your exception class serializable mark it with Serializable attribute and provide a constructor that invokes the base Exception class constructor that takes in SerializationInfo and StreamingContext objects as parameters.
[Serializable]
public class UserAlreadyLoggedInException : Exception
{
public UserAlreadyLoggedInException(SerializationInfo info, StreamingContext context)
: base(info, context)
{
}
}
Note: It is also possible to provide your own custom serialization, which will discuss in a later session.
Complete Example of creating a custom exception:
using System;
using System.Runtime.Serialization;
public class CustomExceptions
{
public static void Main()
{
try
{
throw new UserAlreadyLoggedInException("User Already logged in");
}
catch (UserAlreadyLoggedInException ex)
{
Console.WriteLine(ex.Message);
}
}
}
[Serializable]
public class UserAlreadyLoggedInException : Exception
{
public UserAlreadyLoggedInException(string message)
: base(message)
{
}
public UserAlreadyLoggedInException(string message, Exception innerException)
: base(message, innerException)
{
}
public UserAlreadyLoggedInException(SerializationInfo info, StreamingContext context)
: base(info, context)
{
}
}
Part
43 - C# Tutorial - Exception Handling abuse
Exceptions
are unforeseen errors that occur when a program is running. For example, when an application
is executing a query, the database connection is lost. Exception handling is
generally used to handle these scenarios.
But many a times I have seen programmers using exception handling to implement programming logic which is bad, and this is called as exception handling abuse.
But many a times I have seen programmers using exception handling to implement programming logic which is bad, and this is called as exception handling abuse.
Part
43 - C# Tutorial - Exception Handling abuse
Program using exception handling to implement logical flow:
using System;
public class ExceptionHandlingAbuse
{
public static void Main()
{
try
{
Console.WriteLine("Please enter Numerator");
int Numerator = Convert.ToInt32(Console.ReadLine());
Console.WriteLine("Please enter Denominator");
//Convert.ToInt32() can throw FormatException, if the entered value
//cannot be converted to integer. So use int.TryParse() instead
int Denominator = Convert.ToInt32(Console.ReadLine());
int Result = Numerator / Denominator;
Console.WriteLine("Result = {0}", Result);
}
catch (FormatException)
{
Console.WriteLine("Only numbers are allowed!");
}
catch (OverflowException)
{
Console.WriteLine("Only numbers between {0} & {1} are allowed",
Int32.MinValue, Int32.MaxValue);
}
catch (DivideByZeroException)
{
Console.WriteLine("Denominator cannot be zero");
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
}
In
Part 44 - C# Tutorial - Preventing
exception handling abuse
In Part 43, we have seen what, Exception Handling Abuse is all about. If you haven't watched that video, please click here to watch it now.
In this session we will learn how to prevent exception handling abuse, by rewriting the program, that we have started in Part 43.
Part
44 - C# Tutorial - Preventing exception handling abuse
Rewritten example that doesn't use exception handling to control program's logical flow.
using System;
public class ExceptionHandlingAbuse
{
public static void Main()
{
try
{
Console.WriteLine("Please enter Numerator");
int Numerator;
//int.TryParse() will not throw an exception, instead returns false
//if the entered value cannot be converted to integer
bool isValidNumerator = int.TryParse(Console.ReadLine(), out Numerator);
if (isValidNumerator)
{
Console.WriteLine("Please enter Denominator");
int Denominator;
bool isValidDenominator = int.TryParse(Console.ReadLine(), out Denominator);
if (isValidDenominator && Denominator != 0)
{
int Result = Numerator / Denominator;
Console.WriteLine("Result = {0}", Result);
}
else
{
//Check if the denominator is zero and print a friendly error
//message instead of allowing DivideByZeroException exception
//to be thrown and then printing error message to the user.
if (isValidDenominator && Denominator == 0)
{
Console.WriteLine("Denominator cannot be zero");
}
else
{
Console.WriteLine("Only numbers between {0} && {1} are allowed",
Int32.MinValue, Int32.MaxValue);
}
}
}
else
{
Console.WriteLine("Only numbers between {0} && {1} are allowed",
Int32.MinValue, Int32.MaxValue);
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
}
Part
45 - C# Tutorial - Why Enums
Enums are strongly typed constants. Let's
understand enums with an example. I have a customer class with Name and Gender
properties. Gender is an integer.
0 is an Unknown gender
1 is Male
2 is Female
This program is less readable and maintainable, as it operates on integrals instead of using enums.
In the next session we will replace, these integral numbers with enums, which makes the program better readable and maintainable.
0 is an Unknown gender
1 is Male
2 is Female
This program is less readable and maintainable, as it operates on integrals instead of using enums.
In the next session we will replace, these integral numbers with enums, which makes the program better readable and maintainable.
Part
45 - C# Tutorial - Why Enums
using System;
public class Enums
{
public static void Main()
{
Customer[] customers = new Customer[3];
customers[0] = new Customer()
{
Name = "Mark",
Gender = 1
};
customers[1] = new Customer()
{
Name = "Mary",
Gender = 2
};
customers[2] = new Customer()
{
Name = "Sam",
Gender = 0
};
foreach (Customer customer in customers)
{
Console.WriteLine("Name = {0} && Gender = {1}", customer.Name, GetGender(customer.Gender));
}
}
public static string GetGender(int gender)
{
// The swicth here is less readable because of these integral numbers
switch (gender)
{
case 0:
return "Unknown";
case 1:
return "Male";
case 2:
return "Female";
default:
return "Invalid Data for Gender";
}
}
}
// 0 - Unknown
// 1 - Male
// 2 - Female
public class Customer
{
public string Name { get; set; }
public int Gender { get; set; }
}
Please click here, to watch the video on rewriting the above example using enum, which makes the program more readable and mainatainable
Part
46 - C# Tutorial - Enums Example
Part
46 - C# Tutorial - Enums Example
The program that we have started in Part 45 is now rewritten using Gender enum, which makes it more readable and maintainable.
If you have not watched video on problems of not using enums, please click here to watch it now
using System;
public class Enums
{
public static void Main()
{
Customer[] customers = new Customer[3];
customers[0] = new Customer()
{
Name = "Mark",
Gender = Gender.Male
};
customers[1] = new Customer()
{
Name = "Mary",
Gender = Gender.Female
};
customers[2] = new Customer()
{
Name = "Sam",
Gender = Gender.Unknown
};
foreach (Customer customer in customers)
{
Console.WriteLine("Name = {0} && Gender = {1}", customer.Name, GetGender(customer.Gender));
}
}
public static string GetGender(Gender gender)
{
// The swicth here is now more readable and maintainable because
// of replacing the integral numbers with Gender enum
switch (gender)
{
case Gender.Unknown:
return "Unknown";
case Gender.Male:
return "Male";
case Gender.Female:
return "Female";
default:
return "Invalid Data for Gender";
}
}
}
public enum Gender
{
Unknown = 0,
Male = 1,
Female = 2
}
public class Customer
{
public string Name { get; set; }
public Gender Gender { get; set; }
}
Part
47 - C# Tutorial - Enums in C#
This part is based on Part 45 and 46. I recomend watching these videos first, if you haven't already done so.
Why Enums
Enums in an Example
If a program uses set of integral numbers, consider replacing them with enums, which makes the program more
Readable
Maintainable .
1. Enums are enumerations.
2. Enums are strongly typed constants. Hence, an explicit cast is needed to convert from enum type to an integral type and vice versa. Also, an enum of one type cannot be implicitly assigned to an enum of another type even though the underlying value of their members are the same.
3. The default underlying type of an enum is int.
4. The default value for first element is ZERO and gets incremented by 1.
5. It is possible to customize the underlying type and values.
6. Enums are value types.
7. Enum keyowrd (all small letteres) is used to create enumerations, where as Enum class, contains static GetValues() and GetNames() methods which can be used to list Enum underlying type values and Names.
Part
47 - C# Tutorial - Enums in C#
// Default underlying type is int and the value starts at ZERO
public enum Gender
{
Unknown,
Male,
Female
}
// Gender enum underlying type is now short and the value starts at ONE
public enum Gender : short
{
Unknown = 1,
Male = 2,
Female = 3
}
// Enum values need not be in sequential order. Any valid underlying type value is allowed
public enum Gender : short
{
Unknown = 10,
Male = 22,
Female = 35
}
// This enum will not compile, bcos the maximum value allowed for short data type is 32767.
public enum Gender : short
{
Unknown = 10,
Male = 32768,
Female = 35
}
Note: Use short.MaxValue to find out the maximum value that a short data type can hold
An explicit cast is needed to convert from enum type to an integral type and vice versa.
int i = Gender.Male;
The above line will not compile. A compiler error will be generated stating:
Cannot implicitly convert type 'Gender' to 'int'. An explicit conversion exists (are you missing a cast?)
Gender female = 2;
The above line will also not compile. A slightly different compiler error will be generated stating
Cannot implicitly convert type 'int' to 'Gender'. An explicit conversion exists (are you missing a cast?)
Enum of one type cannot be implicitly assigned to an enum of another type even though the underlying value of their members are the same. An explicit cast is required as shown below.
using System;
public class Enums
{
public static void Main()
{
// This line will not compile. Cannot implicitly convert type 'Season' to 'Gender'.
// An explicit conversion is required.
// Gender gender = Season.Winter;
// This line comiples as we have an explicit cast
Gender gender = (Gender)Season.Winter;
}
}
public enum Gender : int
{
Unknown = 1,
Male = 2,
Female = 3
}
public enum Season : int
{
Winter = 1,
Spring = 2,
Summer = 3
}
enum keyowrd (all small letteres) is used to create enumerations, where as Enum class, contains static GetValues() and GetNames() methods which can be used to list Enum underlying type values and Names.
Sample Program listing all enum member values and Names
using System;
public class Enums
{
public static void Main()
{
int[] Values = (int[])Enum.GetValues(typeof(Gender));
Console.WriteLine("Gender Enum Values");
foreach (int value in Values)
{
Console.WriteLine(value);
}
Console.WriteLine();
string[] Names = Enum.GetNames(typeof(Gender));
Console.WriteLine("Gender Enum Names");
foreach (string Name in Names)
{
Console.WriteLine(Name);
}
}
}
public enum Gender : int
{
Unknown = 1,
Male = 2,
Female = 3
}
In this session we will
1. Understand the difference between Types and Type Members
2. Understand Organising code with regions
In the example below Customer is the Type and private fields(_id, _firstName, _lastName), Properties(Id, FirstName, LastName) and GetFullName() method are type members.
public class Customer
{
#region Private Fields
private int _id;
private string _firstName;
private string _lastName;
#endregion
#region Properties
public int Id
{
get { return _id; }
set { _id = value; }
}
public string FirstName
{
get { return _firstName; }
set { _firstName = value; }
}
public string LastName
{
get { return _lastName; }
set { _lastName = value; }
}
#endregion
#region Methods
public string GetFullName()
{
return this._firstName + " " + this._lastName;
}
#endregion
}
1. Understand the difference between Types and Type Members
2. Understand Organising code with regions
In the example below Customer is the Type and private fields(_id, _firstName, _lastName), Properties(Id, FirstName, LastName) and GetFullName() method are type members.
public class Customer
{
#region Private Fields
private int _id;
private string _firstName;
private string _lastName;
#endregion
#region Properties
public int Id
{
get { return _id; }
set { _id = value; }
}
public string FirstName
{
get { return _firstName; }
set { _firstName = value; }
}
public string LastName
{
get { return _lastName; }
set { _lastName = value; }
}
#endregion
#region Methods
public string GetFullName()
{
return this._firstName + " " + this._lastName;
}
#endregion
}
Part 48 - C# Tutorial - Difference between
Types and Type Members
So, in general classes, structs, enums, interfaces, delegates are called as types and fields, properties, constructors, methods etc., that normally reside in a type are called as type members.
In C# there are 5 different access modifiers.
1. Private
2. Protected
3. Internal
4. Protected Internal
5. Public
Type members can have all the access modifiers, where as types can have only 2 (internal, public) of the 5 access modifiers
In the next session we will discuss about all the access modifiers in detail.
Customer class makes use of regions. Using regions you can expand and collapse sections of your code either manually, or using visual studio Edit -> Outlining -> Toggle All Outlining
Part
49 - C# Tutorial - Access Modifiers - Private, Public, Protected
In
this session we will learn, about different access modifiers that can be
applied for type members.
To better understand this part, please make sure you have already watched video on, Difference between types and type members.
There are 5 different access modifiers in c#.
1. Private
2. Protected
3. Internal
4. Protected Internal
5. Public
Private members are available only with in the containing type, where as public members are available any where. There is no restriction.
To better understand this part, please make sure you have already watched video on, Difference between types and type members.
There are 5 different access modifiers in c#.
1. Private
2. Protected
3. Internal
4. Protected Internal
5. Public
Private members are available only with in the containing type, where as public members are available any where. There is no restriction.
Part
49 - C# Tutorial - Access Modifiers - Private, Public, Protected
In the example below, _id is private, so this member is only available with in the Customer class (Containing Type). It is a compile time error to access _id outside of the Customer Class.
The following line in the MainClass will generate a compiler error stating, 'Customer._id' is inaccessible due to its protection level.
CustomerInstance._id = 101;
On the other hand, since Id is a public member, you can access this member any where, even outside of the Customer class. Infact, we invoke the Id property of the Customer class, in the Main() method as shown below.
CustomerInstance.Id = 101;
Example for Private and Public Members:
public class Customer
{
private int _id;
public int Id
{
get
{
return _id;
}
set
{
_id = value;
}
}
}
public class MainClass
{
private static void Main()
{
Customer CustomerInstance = new Customer();
CustomerInstance.Id = 101;
// Compiler Error : 'Customer._id' is inaccessible due to its protection level
// CustomerInstance._id = 101;
}
}
Protected Members are available, with in the containing type and to the types that derive from the containing type. Let me explain with an example.
Protected Access Modifier Example:
using System;
public class Customer
{
protected int ID = 101;
public void PrintID()
{
//Protected member ID is accessible with in Customer class
Console.WriteLine(this.ID);
}
}
public class CorporateCustomer : Customer
{
public void PrintCustomerID()
{
CorporateCustomer corporateCustomerInstance = new CorporateCustomer();
// Can access the base class protected instance member using the derived class object
Console.WriteLine(corporateCustomerInstance.ID);
// Can access the base class protected instance member using this or base keyword
Console.WriteLine(this.ID);
Console.WriteLine(base.ID);
}
}
public class RetailCustomer
{
public void PrintCustomerID()
{
RetailCustomer retailCustomerInstance = new RetailCustomer();
//RetailCustomer class is not deriving from Customer class, hence it is an error
//to access Customer class protected ID member, using the retailCustomerInstance
//Console.WriteLine(retailCustomerInstance.ID); //Error
//Both these below lines also produce the same Error
//Console.WriteLine(this.ID); // Error
//Console.WriteLine(base.ID); // Error
}
}
Customer class defines a protecetd member ID. CorporateCustomer class derives from the Customer class, so protected ID member is accessible in the Customer class (Containg Type) and also from the CorporateCustomer class (Derived Type).
With in the PrintID() method in the Customer class, Protected member ID is accessible.
Console.WriteLine(this.ID);
There are 3 ways to access, the base class protected member in the derived class as shown below.
1. Using the derived class object.
Console.WriteLine(corporateCustomerInstance.ID);
2. Using the this keyword
Console.WriteLine(this.ID);
3. Using the base keyword
Console.WriteLine(base.ID);
On the other hand, RetailCustomer class is not deriving from Customer class, hence it's a compile time error to access Customer class protected ID member.
Part
50 - C# Tutorial - Internal and Protected Internal Access Modifiers
In
this part we will understand internal and protected internal access modifiers. If you want to learn about private,
public and protected access modifiers, please watch this video
Internal:A member with internal access modifier is available any where with in the containing assembly. It's a compile time error to access, an internal member from outside the containing assembly.
To understand inernal access modifier, we need 2 assemblies. To generate, the 2 assemblies we need follow these steps.
1. Open Solution Explorer (From the Viiew menu, select Solution Explorer)
2. From the Solution Explorer, right click on the project and select Add -> New Project
3. In the Add New Project Dialog Box, Select Visual C# from the Installed Templates section, and select Class Library from the center pane, and then enter AssemblyOne for the Name of the project and click OK.
4. Follow steps 2 & 3, to create AssemblyTwo project.
5. If you have followed these steps correctly, you should now see three projects in the soultion explorer.
Internal:A member with internal access modifier is available any where with in the containing assembly. It's a compile time error to access, an internal member from outside the containing assembly.
To understand inernal access modifier, we need 2 assemblies. To generate, the 2 assemblies we need follow these steps.
1. Open Solution Explorer (From the Viiew menu, select Solution Explorer)
2. From the Solution Explorer, right click on the project and select Add -> New Project
3. In the Add New Project Dialog Box, Select Visual C# from the Installed Templates section, and select Class Library from the center pane, and then enter AssemblyOne for the Name of the project and click OK.
4. Follow steps 2 & 3, to create AssemblyTwo project.
5. If you have followed these steps correctly, you should now see three projects in the soultion explorer.
Part
50 - C# Tutorial - Internal and Protected Internal Access Modifiers
Now, if we build the solution we should have 3 assemblies generated. Two dll's and one exe. To locate the physical assembly follow these steps.
1. Right click on AssemblyOne project, in solution explorer and select Open Folder in Windows Explorer.
2. Open bin folder
3. Now open Debug folder
4. In the Debug folder you should see AssemblyOne.dll, which is the physical assembly.
Copy and paste the following code in class1.cs file of AssemblyOne project using System;
namespace AssemblyOne
{
public class AssemblyOneClassI
{
internal int ID = 999;
}
public class AssemblyOneClassII
{
public void Test()
{
AssemblyOneClassI instance = new AssemblyOneClassI();
// Can access inetrnal member ID, AssemblyOneClassII and AssemblyOneClassI
// are present in the same assembly
Console.WriteLine(instance.ID);
}
}
}
In this example, AssemblyOneClassI has an internal member ID. We can access ID member from AssemblyOneClassII, because this class is also present in the same assembly as AssemblyOneClassI.
Now, Copy and Paste the following code, in Class1.cs of AssemblyTwo project.using System;
using AssemblyOne;
namespace AssemblyTwo
{
public class AssemblyTwoClassI
{
public void Test()
{
AssemblyOneClassI instance = new AssemblyOneClassI();
//Console.WriteLine(instance.ID);
}
}
}
Note: You will get 3 compiler errors at this point. To solve this we need to add an assembly reference. Follow these steps.
1. Expand References folder under AssemblyTwo project, from Solution Explorer.
2. Right Click on References folder and select Add Reference
3. From the Add Reference dialog box, select Projects tab
4. From the list, select AssemblyOne project and click OK.
At, this point all the compiler errors should have gone.
Uncomment the following line from Class1.cs file from AssemblyTwo project and rebuild the solution.
Console.WriteLine(instance.ID);
Now, you will get a compiler error stating 'AssemblyOne.AssemblyOneClassI' does not contain a definition for 'ID' and no extension method 'ID' accepting a first argument of type 'AssemblyOne.AssemblyOneClassI' could be found (are you missing a using directive or an assembly reference?).
This is because, AssemblyTwoClassI is not present in AssemblyOne assembly and hence cannot access the internal ID member defined in AssemblyOne assembly. This proves that internal members are only accessible with in the same assembly. Code outside of the containing assembly cannot access internal members.
Protected Internal:Protected Internal members can be accessed by any code in the assembly in which it is declared, or from within a derived class in another assembly. It is a combination of protected and internal. If you have understood protected and internal, this should be very easy to follow.
Now, change the access modifier from internal to protected internal for ID member in AssemblyOneClassI of class1.cs file in AssemblyOne project.
internal int ID = 999; to protected internal int ID = 999;
Finally modify the code in Class1.cs file in AssemblyTwo project as shown below.using System;
using AssemblyOne;
namespace AssemblyTwo
{
// Make AssemblyTwoClassI inherit from AssemblyOneClassI
public class AssemblyTwoClassI : AssemblyOneClassI
{
public void Test()
{
AssemblyOneClassI instance = new AssemblyOneClassI();
// Access the base class member using the base keyword
Console.WriteLine(base.ID);
}
}
}
So, this shows protected internal ID member, defined in AssemblyOne is accessible in AssemblyTwo.
Part
51 - C# Tutorial - Access Modifiers for types
Please
watch the following videos, before continuing with this part.
Part 48. Difference between Types and Type Members
Part 49. Private, Public and Protected access modifiers
Part 50. Internal and Protected Internal access modifiers
In c# there are 5 different access modifiers.
1. Private
2. Public
3. Protected
4. Internal
5. Protected Internal
Part 48. Difference between Types and Type Members
Part 49. Private, Public and Protected access modifiers
Part 50. Internal and Protected Internal access modifiers
In c# there are 5 different access modifiers.
1. Private
2. Public
3. Protected
4. Internal
5. Protected Internal
Part
51 - C# Tutorial - Access Modifiers for types
You can use all the 5 access modifiers with type members, but types allows only internal and public access modifiers. It is a compile time error to use private, protected and protected internal access modifiers with types.
The following code will generate a compiler error stating Elements defined in a namespace cannot be explicitly declared as private, protected, or protected internal
using System;
namespace Pragim
{
//Error: Cannot mark types with private, protected and protected internal access modifiers
private class MainClass
{
public static void Main()
{
Console.WriteLine("This code will not compile");
}
}
}
Add 2 class library projects to the solution with names AssemblyOne and AssemblyTwo. If you want to learn to do this, please check Part 50 - Access Modifiers - Internal and Protected Internal.
Copy and paste the following code in Class1.cs file of AssemblyOne project.
using System;
namespace AssemblyOne
{
//Class is marked internal. This class is available only with in AssemblyOne
internal class AssemblyOneClass
{
public void Print()
{
Console.WriteLine("Hello");
}
}
}
Copy and paste the following code in Class1.cs file of AssemblyTwo project.
using System;
using AssemblyOne;
namespace AssemblyTwo
{
//Class is marked public. This class is available in any assembly
public class AssemblyTwoClass
{
public void Print()
{
AssemblyOneClass instance = new AssemblyOneClass();
instance.Print();
}
}
}
Add a reference to AssemblyOne project, from AssemblyTwo project. Please check the previous session, to learn about adding project references.
Now build the solution. You will notice the following 4 compiler errors.
1. 'AssemblyOne.AssemblyOneClass' is inaccessible due to its protection level
2. The type 'AssemblyOne.AssemblyOneClass' has no constructors defined
3. 'AssemblyOne.AssemblyOneClass' is inaccessible due to its protection level
4. 'AssemblyOne.AssemblyOneClass' does not contain a definition for 'Print' and no extension method 'Print' accepting a first argument of type 'AssemblyOne.AssemblyOneClass' could be found (are you missing a using directive or an assembly reference?)
All these errors are in AssemblyTwo project, and are related to AssemblyOne.AssemblyOneClass being inaccessible due to its protection level.
Now convert the access modifier of AssemblyOneClass from internal to public and rebuild the solution. Now we get no errors. This shows that internal types are accessible only with in the containing assembly.
Now just remove the public access modifier from AssemblyOneClass and rebuild the solution. You now again get the same 4 errors that we got before. This is because, if you don't specify an access modifier for a type, then by default the access modifier will be internal.
So if you don't specify an access modifier, then for Types the default is internal and for type members it is private.
In this part we will learn
1. The purpose of attributes
2. Using an attribute
3. Customizing attribute using parameters
Purpose: Attributes allow you to add declarative information to your programs. This information can then be queried at runtime using reflection.
1. The purpose of attributes
2. Using an attribute
3. Customizing attribute using parameters
Purpose: Attributes allow you to add declarative information to your programs. This information can then be queried at runtime using reflection.
Part 52 - C# Tutorial - Attributes in C#
There are several Pre-defined Attributes provided by .NET. It is also possible to create your own Custom Attributes. Creating custom attributes is beyond the scope of this article.
A few pre-defined attributes with in the .NET framework.
Obsolete - Marks types and type members outdated
WebMethod - To expose a method as an XML Web service method
Serializable - Indicates that a class can be serialized
Example program using pre defined Obsolete attribute:
Obsolete attribute can be used with types or type members that are obsolete (Outdated). If a developer uses a type or a type member that is decorated with obsolete attribute, the compiler issues a warning or an error depending on how the attribute is configured.
In this sample program, Add(int FirstNumber, int SecondNumber) method is decorated with [Obsolete] attribute. If you compile this program, in the output window you will see a warning message (Compile complete -- 0 errors, 1 warnings). Also, visual studio, shows a green squiggly line under the Add(int FirstNumber, int SecondNumber) method. If you hover the mouse over the squiggly line, you should see the warning message.
Note: If you don't see the warning message (Compile complete -- 0 errors, 1 warnings), rebuild the soultion.
using System;
using System.Collections.Generic;
public class MainClass
{
private static void Main()
{
Calculator.Add(10, 15);
}
}
public class Calculator
{
[Obsolete]
public static int Add(int FirstNumber, int SecondNumber)
{
return FirstNumber + SecondNumber;
}
public static int Add(List<int> Numbers)
{
int Sum = 0;
foreach (int Number in Numbers)
{
Sum = Sum + Number;
}
return Sum;
}
}
The warning message says 'Calculator.Add(int, int)' is obsolete. However, this message is not completely useful, because it says 'Calculator.Add(int, int)' is obsolete, but not tell us which other method should we be using instead. So this is when we can customize, the warning message using attribute parameters.
The intention of the developer of Calculator class is that, he wanted us to use Add(List<int> Numbers), instead of int Add(int FirstNumber, int SecondNumber). To communicate this message we can customize the warning message using attribute parameters as shown below. With this customization we are not only communicating that Add(int FirstNumber, int SecondNumber) method is obsolete, we are also telling to use the alternative method that is available.
[Obsolete("Use Add(List<int> Numbers) instead")]
public static int Add(int FirstNumber, int SecondNumber)
If you want to generate a compiler error instead of warning, pass true for the bool error parameter of the Obsolete attribute as shown below. Now, we can't even compile the program.
[Obsolete("Use Add(List<int> Numbers) instead", true)]
public static int Add(int FirstNumber, int SecondNumber)
Finally, If you right click on Obsolete attribute and select Go To Definition, you will see that, an attribute is nothing but a class that inherits from System.Attribute base class.
Part 53 - C# Tutorial
- Reflection
Reflection
is the ability of inspecting an assemblie's metadata at runtime. It is
used to find all types in an assembly and/or dynamically invoke methods in an
assembly. This includes information about the type, properties, methods, and
events of an object. With Reflection, we can dynamically create an instance of
a type, bind the type to an existing object, or get the type from an existing
object and invoke its methods or access its fields and properties.There are
several uses of reflection.
Part 53 - C# Tutorial - Reflection
1. When you drag and drop a button on a win forms or an asp.net application. The properties window uses reflection to show all the properties of the Button class. So,reflection is extensivley used by IDE or a UI designers.
2. Late binding can be achieved by using reflection. You can use reflection to dynamically create an instance of a type, about which we don't have any information at compile time. So, reflection enables you to use code that is not available at compile time.
3. Consider an example where we have two alternate implementations of an interface. You want to allow the user to pick one or the other using a config file. With reflection, you can simply read the name of the class whose implementation you want to use from the config file, and instantiate an instance of that class. This is another example for late binding using reflection.
So, in short reflection can be used for type discovery (i.e finding methods, properties, events, fields, constructors etc) and late binding.
In this session we will learn how to list a specifc class methods, properties, fields etc using reflection. All the classes and methods related to reflection are present in System.Reflection namespace.
The Type class is the most importanct class.
Consider the Customer class example. This class has got
1. Two constructors
2. Two auto implemeneted properties
3. Two methods
using System;
using System.Reflection;
namespace Pragim
{
public class MainClass
{
private static void Main()
{
// Get the Type Using GetType() static method
Type T = Type.GetType("Pragim.Customer");
// Print the Type details
Console.WriteLine("Full Name = {0}",T.FullName);
Console.WriteLine("Just the Class Name = {0}",T.Name);
Console.WriteLine("Just the Namespace = {0}", T.Namespace);
Console.WriteLine();
// Print the list of Methods
Console.WriteLine("Methods in Customer Class");
MethodInfo[] methods = T.GetMethods();
foreach (MethodInfo method in methods)
{
// Print the Return type and the name of the method
Console.WriteLine(method.ReturnType.Name + " " + method.Name);
}
Console.WriteLine();
// Print the Properties
Console.WriteLine("Properties in Customer Class");
PropertyInfo[] properties = T.GetProperties();
foreach (PropertyInfo property in properties)
{
// Print the property type and the name of the property
Console.WriteLine(property.PropertyType.Name + " " + property.Name);
}
Console.WriteLine();
// Print the Constructors
Console.WriteLine("Constructors in Customer Class");
ConstructorInfo[] constructors = T.GetConstructors();
foreach (ConstructorInfo constructor in constructors)
{
Console.WriteLine(constructor.ToString());
}
}
}
public class Customer
{
public int Id { get; set; }
public string Name { get; set; }
public Customer(int ID, string Name)
{
this.Id = ID;
this.Name = Name;
}
public Customer()
{
this.Id = -1;
this.Name = string.Empty;
}
public void PrintID()
{
Console.WriteLine("ID = {0}", this.Id);
}
public void PrintName()
{
Console.WriteLine("Name = {0}", this.Name);
}
}
}
In this example to get the type of customer class we have used GetType() static method defined on the Type class. We pass in the fully qualified name of the type including the namespace as a parameter to the GetType() method.
Type T = Type.GetType("Pragim.Customer");
To get the type information we have the following 2 ways as well.
Use typeof keyowrd
Type T = typeof(Customer);
Use GetType() on the instance of the customer class.
Customer C1 = new Customer();
Type T = C1.GetType();
To get the methods information, we use Type.GetMethods(), which returns MethodInfo[] array and along the same lines we use Type.GetProperties() to get properties information, but Type.GetProperties() returns PropertyInfo[] array.
Part
54 - C# Tutorial - Reflection Example
This session is based
on the previous session - Part 53 Reflection in C#. Please watch part 53,
before continuing with this session.In this session we will develop a simple winforms application. Please design the form as shown in the image below.
Subscribe to:
Posts (Atom)