C# and ASP.NET Core Concepts: Essential Development Topics
C# Fundamentals
CLR and .NET Applied Technologies
The Common Language Runtime (CLR) is the heart of the .NET framework, providing the execution environment for .NET applications.
CLR (Common Language Runtime)
It is the execution engine for .NET applications. It manages memory, code execution, security, and exception handling, among other services.
Features of CLR
- Memory Management: Automatic allocation and deallocation of memory via garbage collection, preventing memory leaks.
- Code Access Security (CAS): Controls access to system resources based on the identity of the code.
- Exception Handling: Provides a unified model for detecting and recovering from errors during program execution.
- Cross-language Integration: Enables different .NET languages (e.g., C#, VB.NET) to work together seamlessly.
- Just-In-Time (JIT) Compilation: Converts Intermediate Language (IL) code into native machine code at runtime, optimizing performance.
Four Applied Technologies in .NET
The .NET ecosystem supports various application development types:
- ASP.NET: For building dynamic web applications and services.
- ADO.NET: Provides data access services for connecting to and manipulating databases.
- Windows Forms (WinForms): A framework for creating traditional desktop GUI applications for Windows.
- WPF (Windows Presentation Foundation): A newer, richer UI framework for desktop applications, utilizing XAML for declarative UI design.
C# Type-Safety and .NET Technologies
C# is designed as a type-safe language, which contributes significantly to robust and reliable software development.
Type-Safety in C#
C# is considered type-safe because it enforces strict type checking at both compile-time and runtime. This means:
- You cannot use variables without declaring their types.
- Implicit conversions between incompatible types are generally disallowed, preventing unexpected data loss or errors.
- It helps prevent invalid type conversions and memory-related errors, leading to more stable applications.
(For more on .NET technologies, refer to the “CLR and .NET Applied Technologies” section.)
C# Value Types vs. Reference Types
Understanding the distinction between value types and reference types is fundamental to C# programming, impacting memory management and behavior.
Value Type
- Stores actual data directly in its memory location.
- Typically stored on the stack.
- Examples:
int
,float
,double
,struct
,enum
.
Reference Type
- Stores a reference (memory address) to the data, which is stored elsewhere.
- Typically stored on the heap.
- Examples:
class
,string
,array
,interface
,delegate
.
Example
int a = 5; // Value Type: 'a' directly holds the value 5
string name = "Lalit"; // Reference Type: 'name' holds a reference to the string "Lalit" on the heap
C# Contextual Keywords and Jagged Arrays
Contextual keywords add flexibility to the language, while jagged arrays offer dynamic multi-dimensional data structures.
Five Contextual Keywords in C#
Contextual keywords are special words that have meaning only in a specific code context, allowing them to be used as identifiers elsewhere:
var
: Used for implicitly typed local variables.yield
: Used in iterator blocks to provide a value to the enumerator object.async
: Marks a method as asynchronous, enabling the use ofawait
.await
: Pauses the execution of an async method until an awaited task completes.partial
: Allows splitting the definition of a class, struct, or interface across multiple source files.
Jagged Array Program
A jagged array is an array of arrays, where each inner array can be of a different size.
using System;
class Program
{
static void Main()
{
int[][] arr = new int[3][]; // Declare a jagged array with 3 rows
arr[0] = new int[] { 10, 20 }; // First row has 2 elements
arr[1] = new int[] { 5, 15, 25 }; // Second row has 3 elements
arr[2] = new int[] { 7 }; // Third row has 1 element
for (int i = 0; i < arr.Length; i++)
{
int sum = 0;
Console.Write($"Row {i + 1}: ");
foreach (int val in arr[i])
{
Console.Write(val + " ");
sum += val;
}
Console.WriteLine($"=> Sum: {sum}");
}
}
}
C# String Interpolation and Parameter Passing
These features enhance code readability and control how data is handled when passed to methods.
String Interpolation
Introduced in C# 6.0, string interpolation provides a concise and readable syntax for creating formatted strings by embedding expressions directly within string literals, prefixed with $
.
string name = "Lalit";
Console.WriteLine($"Hello {name}"); // Output: Hello Lalit
int age = 30;
Console.WriteLine($"Name: {name}, Age: {age}"); // Output: Name: Lalit, Age: 30
Value vs. Reference Passing
C# supports different mechanisms for passing arguments to methods, affecting whether the original variable is modified.
using System;
class Program
{
// Method to demonstrate value passing
static void ModifyValue(int a)
{
a = 20; // Changes only the local copy of 'a'
}
// Method to demonstrate reference passing using 'ref' keyword
static void ModifyRef(ref int a)
{
a = 20; // Changes the original variable 'a'
}
static void Main()
{
int x = 10;
Console.WriteLine($"Before ModifyValue: x = {x}"); // Output: 10
ModifyValue(x);
Console.WriteLine($"After ModifyValue: x = {x}"); // Output: 10 (x remains unchanged)
Console.WriteLine($"\nBefore ModifyRef: x = {x}"); // Output: 10
ModifyRef(ref x);
Console.WriteLine($"After ModifyRef: x = {x}"); // Output: 20 (x is changed)
}
}
C# Namespace Usage and Organization
Namespaces are fundamental for organizing code and preventing naming conflicts in larger C# projects.
Why Use Namespaces?
- To Organize Code: Group related classes, structs, interfaces, enums, and delegates logically.
- To Avoid Name Conflicts: Prevent ambiguity when different libraries or parts of an application define types with the same name.
Creating and Using a Namespace
using System; // Using directive to import the System namespace
namespace MyProject // Declaring a namespace
{
class Test
{
public void Greet() => Console.WriteLine("Hello from MyProject!");
}
}
class Program
{
static void Main()
{
// Accessing the Test class using its fully qualified name
MyProject.Test t = new MyProject.Test();
t.Greet();
// Alternatively, using a 'using' directive for MyProject:
// using MyProject;
// Test t2 = new Test();
// t2.Greet();
}
}
C# Object-Oriented Programming
C# Indexers, Properties, and Generic Classes
Indexer vs. Properties: Key Differences
Feature | Indexer | Property |
---|---|---|
Syntax | this[index] | get/set |
Purpose | Access data like an array | Access data like a variable |
Usage | obj[0] | obj.Name |
Generic Class Example
Generic classes allow you to define classes with type parameters, enabling reusable code that works with different data types.
class MyBox<T>
{
public T Value;
public void Show() => Console.WriteLine(Value);
}
class Program
{
static void Main()
{
MyBox<int> b1 = new MyBox<int> { Value = 100 };
MyBox<string> b2 = new MyBox<string> { Value = "Hello" };
b1.Show();
b2.Show();
}
}
C# Delegates and Operator Overloading
Delegates provide a way to treat methods as objects, while operator overloading allows custom behavior for operators.
Types of Delegates
Delegates are type-safe function pointers. Key types include:
- Single-cast Delegate: Points to a single method.
- Multi-cast Delegate: Can point to multiple methods, which are invoked sequentially.
- Generic Delegate (
Func
,Action
,Predicate
): Predefined delegates for common scenarios, reducing the need for custom delegate declarations.
Time Class Program: Operator Overloading Example
using System;
class Time
{
public int Hours, Minutes, Seconds;
public Time(int h, int m, int s)
{
Hours = h; Minutes = m; Seconds = s;
}
public void DisplayTime() => Console.WriteLine($"{Hours:D2}:{Minutes:D2}:{Seconds:D2}");
public static Time operator +(Time t1, Time t2)
{
int s = t1.Seconds + t2.Seconds;
int m = t1.Minutes + t2.Minutes + s / 60;
int h = t1.Hours + t2.Hours + m / 60;
return new Time(h % 24, m % 60, s % 60);
}
public static bool operator >(Time t1, Time t2)
{
if (t1.Hours != t2.Hours) return t1.Hours > t2.Hours;
if (t1.Minutes != t2.Minutes) return t1.Minutes > t2.Minutes;
return t1.Seconds > t2.Seconds;
}
public static bool operator <(Time t1, Time t2)
{
if (t1.Hours != t2.Hours) return t1.Hours < t2.Hours;
if (t1.Minutes != t2.Minutes) return t1.Minutes < t2.Minutes;
return t1.Seconds < t2.Seconds;
}
}
C# Operator Overloading: Binary and Relational
Operator overloading enables you to define how standard operators behave when applied to instances of your custom classes.
Operator Overloading Definition
Allows defining custom behavior for operators when used with user-defined types, enhancing readability and natural syntax for complex objects.
Program: Point Class Example
using System;
class Point
{
public int X, Y;
public Point(int x, int y) { X = x; Y = y; }
public static Point operator +(Point a, Point b)
{
return new Point(a.X + b.X, a.Y + b.Y);
}
public static bool operator >(Point a, Point b)
{
// Example: Compare based on sum of coordinates
return (a.X + a.Y) > (b.X + b.Y);
}
public static bool operator <(Point a, Point b)
{
// Example: Compare based on sum of coordinates
return (a.X + a.Y) < (b.X + b.Y);
}
public void Display() => Console.WriteLine($"({X}, {Y})");
}
class Program
{
static void Main()
{
Point p1 = new Point(2, 3);
Point p2 = new Point(1, 5);
Point sum = p1 + p2;
sum.Display(); // Output: (3, 8)
Console.WriteLine(p1 > p2 ? "p1 is greater" : "p2 is greater"); // (2+3=5) > (1+5=6) is false, so "p2 is greater"
}
}
C# Interfaces and Multiple Inheritance
Interfaces are a cornerstone of C# OOP, enabling polymorphism and a form of multiple inheritance for behavior definition.
Interface
A contract that defines a set of members (methods, properties, events, indexers) that a class or struct must implement. Key characteristics:
- Contains only method/property declarations (no implementation in older C# versions; C# 8.0+ allows default implementations).
- Cannot be instantiated directly.
Multiple Inheritance via Interface
C# does not support multiple inheritance of implementation from multiple base classes. However, it supports multiple inheritance of type by allowing a class to implement multiple interfaces, thereby inheriting multiple contracts.
Example
using System;
interface IA { void Show(); }
interface IB { void Display(); }
class Demo : IA, IB
{
public void Show() => Console.WriteLine("From IA");
public void Display() => Console.WriteLine("From IB");
}
class Program
{
static void Main()
{
Demo d = new Demo();
d.Show();
d.Display();
}
}
C# Abstract Classes and the base
Keyword
Abstract classes provide a blueprint for derived classes, while the base
keyword facilitates interaction with the parent class.
Uses of the base
Keyword
The base
keyword is used to access members of the base class from within a derived class:
- Access Base Class Members: Call methods, properties, or fields defined in the base class that might be hidden or overridden in the derived class.
- Call Base Class Constructor: Invoke a constructor of the base class from a derived class’s constructor, ensuring proper initialization of the base part of the object.
Program with Abstract Class
An abstract class cannot be instantiated and may contain abstract members (methods, properties) that must be implemented by non-abstract derived classes.
using System;
abstract class Shape // Abstract class
{
public abstract void Draw(); // Abstract method (no implementation)
}
class Circle : Shape // Derived class implementing the abstract method
{
public override void Draw()
{
Console.WriteLine("Drawing Circle");
}
}
class Program
{
static void Main()
{
Shape s = new Circle(); // Cannot instantiate Shape directly, but can use a derived class
s.Draw(); // Output: Drawing Circle
}
}
C# Virtual Methods and Multi-Level Inheritance
Virtual methods enable polymorphism, allowing derived classes to provide specific implementations, often seen in inheritance hierarchies.
Virtual Method
A method declared in a base class using the virtual
keyword. It can be overridden in derived classes using the override
keyword, allowing for polymorphic behavior at runtime.
Program: Multi-Level Inheritance Example
using System;
class Animal
{
public virtual void Speak() => Console.WriteLine("Animal speaks");
}
class Dog : Animal // Dog inherits from Animal
{
public override void Speak() => Console.WriteLine("Dog barks");
}
class Puppy : Dog // Puppy inherits from Dog (multi-level inheritance)
{
public override void Speak() => Console.WriteLine("Puppy yaps");
}
class Program
{
static void Main()
{
Animal a = new Puppy(); // Polymorphism: An Animal reference pointing to a Puppy object
a.Speak(); // Output: Puppy yaps (runtime binding to Puppy's Speak method)
}
}
C# Static Classes and Constructors
Static members and classes are fundamental concepts for utility classes and shared data in C#.
Static Class
Characteristics of a static class:
- Cannot be instantiated (you cannot create objects of a static class).
- Contains only static members (fields, methods, properties, events).
- Sealed implicitly (cannot be inherited).
Static Constructor
Key aspects of a static constructor:
- Initializes static data or performs actions exactly once when the class is first loaded into memory.
- Runs automatically before any static members are accessed or any instance of the class is created.
- Cannot take access modifiers or parameters.
Example: Logger Static Class
using System;
static class Logger
{
static int count;
static Logger()
{
count = 0;
Console.WriteLine("Static Constructor Called");
}
public static void Log(string msg)
{
count++;
Console.WriteLine($"{count}: {msg}");
}
}
class Program
{
static void Main()
{
Logger.Log("Program started");
Logger.Log("Program running");
}
}
C# Exception Handling
C# Exception Handling: System vs. Application Exceptions
Understanding the difference between system and application exceptions is crucial for robust error handling in C# applications.
System Exception
Errors generated by the .NET runtime, such as NullReferenceException
or IndexOutOfRangeException
. These typically indicate fundamental issues within the application or environment.
Program with Application Exception
Application exceptions are custom exceptions or instances of ApplicationException
that are thrown by the application code to signal specific business logic errors.
using System;
class Program
{
static void Main()
{
Console.Write("Enter Balance: ");
double balance = Convert.ToDouble(Console.ReadLine());
Console.Write("Enter Withdraw Amount: ");
double withdraw = Convert.ToDouble(Console.ReadLine());
try
{
if (withdraw > balance)
throw new ApplicationException("Insufficient Balance!");
else
Console.WriteLine($"Remaining Balance: {balance - withdraw}");
}
catch (ApplicationException ex)
{
Console.WriteLine("Error: " + ex.Message);
}
}
}
C# Exception Handling: Common Scenarios
An exception is an event that disrupts the normal flow of program execution. C# provides structured exception handling mechanisms to manage these errors gracefully.
Program: Divide by Zero and Index Out of Range
using System;
class Program
{
static void Main()
{
try
{
int a = 10, b = 0;
Console.WriteLine(a / b); // This will throw DivideByZeroException
int[] arr = { 1, 2, 3 };
Console.WriteLine(arr[5]); // This will throw IndexOutOfRangeException
}
catch (DivideByZeroException ex)
{
Console.WriteLine("Divide By Zero Error: " + ex.Message);
}
catch (IndexOutOfRangeException ex)
{
Console.WriteLine("Index Error: " + ex.Message);
}
}
}
LINQ (Language Integrated Query)
LINQ Operators and Data Manipulation
Language Integrated Query (LINQ) provides a powerful way to query data from various sources using a consistent syntax.
Five Standard LINQ Operators
Commonly used LINQ operators include:
Where
: Filters a sequence of values based on a predicate.Select
: Projects each element of a sequence into a new form.OrderByDescending
: Sorts the elements of a sequence in descending order.Sum
: Calculates the sum of a sequence of numeric values.Average
: Calculates the average of a sequence of numeric values.
LINQ Program: Employee Salary Example
using System;
using System.Linq;
class Employee
{
public string Name;
public double Salary;
}
class Program
{
static void Main()
{
Employee[] employees = {
new Employee { Name = "A", Salary = 5000 },
new Employee { Name = "B", Salary = 6000 },
new Employee { Name = "C", Salary = 5500 },
new Employee { Name = "D", Salary = 4800 },
new Employee { Name = "E", Salary = 7000 }
};
var totalSalary = employees.Sum(e => e.Salary);
Console.WriteLine($"Total Salary: {totalSalary}");
var sorted = employees.OrderByDescending(e => e.Salary);
foreach (var emp in sorted)
Console.WriteLine($"{emp.Name}: {emp.Salary}");
}
}
LINQ Query Expressions: Filtering Data
LINQ query expressions offer a SQL-like syntax for querying collections in C#, making data retrieval intuitive.
Query Expression Definition
A concise way to query collections using LINQ syntax, often preferred for its readability when dealing with complex queries.
Program: Filter Students Example
using System;
using System.Linq;
using System.Collections.Generic;
class Student
{
public string Name, Address, Campus;
}
class Program
{
static void Main()
{
List<Student> students = new List<Student>
{
new Student { Name="Rita", Address="Kirtipur", Campus="Patan Multiple Campus" },
new Student { Name="Sita", Address="Bhaktapur", Campus="Patan Multiple Campus" },
new Student { Name="Gita", Address="Kirtipur", Campus="TU" }
};
var result = from s in students
where s.Address == "Kirtipur" && s.Campus == "Patan Multiple Campus"
select s;
foreach (var student in result)
Console.WriteLine(student.Name);
}
}
ADO.NET and Database Operations
ADO.NET: ExecuteScalar vs. ExecuteReader
These methods are fundamental for interacting with databases using ADO.NET, each serving a distinct purpose.
Method Differences
Method | Purpose |
---|---|
ExecuteScalar() | Returns a single scalar value (e.g., COUNT, SUM, MAX) from the first row and first column of the result set. |
ExecuteReader() | Reads multiple rows and columns of data using a DataReader object, providing forward-only, read-only access. |
Bank Database Program: Insert and Display Example
using System;
using MySql.Data.MySqlClient;
class Program
{
static void Main()
{
string connStr = "server=localhost;user=root;database=Bank;password=yourpass;";
using (MySqlConnection conn = new MySqlConnection(connStr))
{
conn.Open();
// Insert data
for (int i = 1; i <= 5; i++)
{
string insert = "INSERT INTO Customer (Account_no, Name, Address, Balance) " +
"VALUES (@Acc, @Name, @Addr, @Bal)";
var cmd = new MySqlCommand(insert, conn);
cmd.Parameters.AddWithValue("@Acc", 100 + i);
cmd.Parameters.AddWithValue("@Name", $"User{i}");
cmd.Parameters.AddWithValue("@Addr", "Kathmandu");
cmd.Parameters.AddWithValue("@Bal", 1000 * i + 2000);
cmd.ExecuteNonQuery();
}
Console.WriteLine("5 customers inserted.");
// Display data using ExecuteReader
string query = "SELECT Account_no, Name, Balance FROM Customer WHERE Balance > 5000";
var reader = new MySqlCommand(query, conn).ExecuteReader();
Console.WriteLine("\nCustomers with Balance > 5000:");
while (reader.Read())
{
Console.WriteLine($"{reader["Account_no"]}, {reader["Name"]}, {reader["Balance"]}");
}
reader.Close();
}
}
}
ADO.NET: ExecuteReader vs. ExecuteNonQuery
These two ADO.NET methods are essential for different types of database operations: reading data versus modifying it.
Method Differences
Feature | ExecuteReader() | ExecuteNonQuery() |
---|---|---|
Purpose | Read data (e.g., SELECT statements) | Modify data (e.g., INSERT , UPDATE , DELETE , CREATE TABLE ) |
Return Type | DataReader object (e.g., MySqlDataReader ) | int (representing the number of rows affected by the operation) |
Program: MySQL Product Table Example
using System;
using MySql.Data.MySqlClient;
class Program
{
static void Main()
{
string connStr = "server=localhost;user=root;database=Company;password=yourpass;";
using (MySqlConnection conn = new MySqlConnection(connStr))
{
conn.Open();
// Read data using ExecuteReader
string query = "SELECT ProductId, ProductName, UnitPrice FROM Product WHERE UnitPrice > 1000";
MySqlCommand cmd = new MySqlCommand(query, conn);
MySqlDataReader reader = cmd.ExecuteReader();
Console.WriteLine("Products with UnitPrice > 1000:");
while (reader.Read())
{
Console.WriteLine($"{reader["ProductId"]} - {reader["ProductName"]} - {reader["UnitPrice"]}");
}
reader.Close();
// Update data using ExecuteNonQuery
string update = "UPDATE Product SET UnitPrice = 4500 WHERE ProductId = 50";
MySqlCommand updateCmd = new MySqlCommand(update, conn);
int rows = updateCmd.ExecuteNonQuery();
Console.WriteLine($"{rows} row(s) updated.");
}
}
}
ASP.NET Web Forms
ASP.NET Simple Interest Calculator Form
This example demonstrates basic form navigation in ASP.NET using two pages: one for input and another for displaying the result.
Page 1: Input Form (InterestForm.aspx
)
<form method="post" action="Result.aspx">
Principal: <input type="text" name="principal" /><br />
Rate: <input type="text" name="rate" /><br />
Time: <input type="text" name="time" /><br />
<input type="submit" value="Calculate Interest" />
</form>
Page 2: Display Result (Result.aspx
)
<%
double p = double.Parse(Request.Form["principal"]);
double r = double.Parse(Request.Form["rate"]);
double t = double.Parse(Request.Form["time"]);
double si = (p * r * t) / 100;
Response.Write($"Simple Interest: {si}");
%>
ASP.NET Web Server Controls and Forms
ASP.NET Web Server Controls provide an object-oriented way to create user interfaces for web applications, abstracting away much of the HTML and client-side scripting.
Common Web Server Controls
TextBox
: For single-line or multi-line text input.Button
: To trigger server-side events.DropDownList
: For selecting an item from a predefined list.RadioButton
: For selecting a single option from a group.Label
: To display static text.
Page 1: Student Registration Form (StudentForm.aspx
)
<%@ Page Language="C#" AutoEventWireup="true" %>
<html>
<body>
<form runat="server" method="post" action="StudentResult.aspx">
Name: <asp:TextBox ID="txtName" runat="server" /><br />
Age: <asp:TextBox ID="txtAge" runat="server" /><br />
Gender:
<asp:RadioButton ID="rbMale" GroupName="gender" Text="Male" runat="server" />
<asp:RadioButton ID="rbFemale" GroupName="gender" Text="Female" runat="server" /><br />
<asp:Button ID="btnSubmit" Text="Submit" runat="server" PostBackUrl="StudentResult.aspx" />
</form>
</body>
</html>
Page 2: Display Student Result (StudentResult.aspx
)
<%@ Page Language="C#" AutoEventWireup="true" %>
<html>
<body>
<%
string name = Request.Form["txtName"];
string age = Request.Form["txtAge"];
// Determine gender based on which radio button was checked
string gender = Request.Form["rbMale"] != null ? "Male" : (Request.Form["rbFemale"] != null ? "Female" : "Not Specified");
Response.Write($"Name: {name}<br/>Age: {age}<br/>Gender: {gender}");
%>
</body>
</html>