Lecture No. 09
Dated: 21-05-2025
Sometimes, some actions need to be cleaned up, regardless if they succeed or fail e.g. closing a filestream
must happen.
These are good candidates for finally
blocks.
using System;
using System.IO;
class FinallyDemo {
static void Main(string[] args) {
FileStream outStream = null;
FileStream inStream = null;
try {
outStream = File.OpenWrite("destinationfile.txt");
inStream = File.OpenRead("bogusinputfile.txt");
}
catch (Exception ex) {
Console.WriteLine(ex.ToString());
}
finally {
if (outStream != null) {
outStream.Close();
Console.WriteLine("outstream closed.");
}
if (inStream != null) {
inStream.Close();
Console.WriteLine("instream closed.");
}
}
}
}
Attributes
Attributes
are classes which add declarative information and metadata to your programs.
Example
DllImportAttribute
allows a program to communicate withWin32
libraries.ObsoleteAttribute
causes a compile time warning to appear.
When a C#
program is compiled, it creates a filed called assembly
which is normally an executable or DLL
library which are self describing because they have metadata.
Through reflection
, a program's attributes can be retrieved from its assembly metadata
.
using System;
using System.Threading; // Required for [STAThread]
class BasicAttributeDemo {
[Obsolete] // The "Attribute" part is optional
public void MyFirstDeprecatedMethod() {
Console.WriteLine("Called MyFirstDeprecatedMethod().");
}
[ObsoleteAttribute] // This is the full name of the attribute
public void MySecondDeprecatedMethod() {
Console.WriteLine("Called MySecondDeprecatedMethod().");
}
[Obsolete("You shouldn't use this method anymore.")]
public void MyThirdDeprecatedMethod() {
Console.WriteLine("Called MyThirdDeprecatedMethod().");
}
// Make the program thread-safe for COM
[STAThread]
static void Main(string[] args) {
BasicAttributeDemo attrDemo = new BasicAttributeDemo();
attrDemo.MyFirstDeprecatedMethod();
attrDemo.MySecondDeprecatedMethod();
attrDemo.MyThirdDeprecatedMethod();
}
}
After compiling, we get
G:\Projects\test\BasicAttributeDemo\Program.cs(29,9): warning CS0612: 'BasicAttributeDemo.MyFirstDeprecatedMethod()' is obsolete
G:\Projects\test\BasicAttributeDemo\Program.cs(30,9): warning CS0612: 'BasicAttributeDemo.MySecondDeprecatedMethod()' is obsolete
G:\Projects\test\BasicAttributeDemo\Program.cs(31,9): warning CS0618: 'BasicAttributeDemo.MyThirdDeprecatedMethod()' is obsolete: 'You shouldn't use this method anymore.'
Called MyFirstDeprecatedMethod().
Called MySecondDeprecatedMethod().
Called MyThirdDeprecatedMethod().
STAthread
full form
STA stands for Single Threaded Apartment model which is used for communicating with unmanaged COM
.
Parameters
They can be either named or positional.
Examples
The following gives an error
[Obsolete("You shouldn’t use this method anymore.", true)]
public void mythirddeprecatedmethod()
In terms of ordering, the positional parameters come before named parameters, however, named parameters themselves can be of any order.
[DllImport("User32.dll", entrypoint="messagebox")]
using System;
using System.Runtime.InteropServices; // Often needed for STAThread, though Threading is more direct.
using System.Threading; // Explicitly for STAThread
[assembly: CLSCompliant(true)] // This attribute applies to the entire assembly
public class AttributeTargetDemo {
// uint is not CLS-compliant for public methods if the assembly is marked CLSCompliant(true).
// The compiler will issue a warning.
public void NonCLSCompliantMethod(uint nclsParam) {
Console.WriteLine("Called NonCLSCompliantMethod().");
}
[STAThread] // This attribute applies to the Main method, indicating it's a single-threaded apartment.
static void Main(string[] args) {
uint myUint = 0; // uint is fine for local variables
AttributeTargetDemo tgtDemo = new AttributeTargetDemo();
tgtDemo.NonCLSCompliantMethod(myUint);
}
}
Enums
enum
of one type may not be implicitly assigned to an enum
of another type.
The C#
enum
inherits from Base Class Library
's Enum
.
using System;
// Declares the enum
public enum Volume {
Low,
Medium,
High
}
// Demonstrates how to use the enum
class EnumSwitch {
static void Main() {
// Create and initialize an instance of enum type
Volume myVolume = Volume.Medium;
// Make decision based on enum value
switch (myVolume) {
case Volume.Low:
Console.WriteLine("The volume has been turned down.");
break;
case Volume.Medium:
Console.WriteLine("The volume is in the middle.");
break;
case Volume.High:
Console.WriteLine("The volume has been turned up.");
break;
}
Console.ReadLine(); // Keeps the console window open until a key is pressed
}
}
The default underlying type for an enum
is int
but it can be changed to
byte
sbyte
short
ushort
int
uint
long
ulong
using System;
public enum Volume : byte {
Low = 1,
Medium,
High
}
class EnumConverter {
static void Main(string[] args) {
// --- To convert user input to enum ---
Console.Write("Enter a volume (e.g., 1 for Low, 2 for Medium, 3 for High): ");
string volString = Console.ReadLine();
if (int.TryParse(volString, out int volInt)) {
// Perform explicit cast from int to Volume enum type
// It's good practice to check if the integer value is actually a valid enum member.
if (Enum.IsDefined(typeof(Volume), (byte)volInt)) {
Volume myVolume = (Volume)volInt;
Console.WriteLine($"You selected: {myVolume} ({(byte)myVolume})");
}
else {
Console.WriteLine("Invalid volume entered.");
}
}
else {
Console.WriteLine("Invalid input. Please enter a number.");
}
Console.WriteLine("\n--- Enumerating Enum Members and Values ---");
// --- To get a list of member names from Volume enum, figure out the numeric value, and display ---
Console.WriteLine("Member Names and Values:");
foreach (string volumeName in Enum.GetNames(typeof(Volume))) {
// Enum.Parse returns an object, so cast it to the enum type before casting to the underlying type (byte)
Volume volumeEnum = (Volume)Enum.Parse(typeof(Volume), volumeName);
Console.WriteLine($"Volume Member: {volumeName}\n Value: {(byte)volumeEnum}");
}
Console.WriteLine("\n--- Enumerating Enum Values and Member Names ---");
// --- To get all values (numeric values) from the Volume enum type, figure out member name, and display ---
Console.WriteLine("Numeric Values and Member Names:");
foreach (byte val in Enum.GetValues(typeof(Volume))) {
Console.WriteLine($"Volume Value: {val}\n Member: {Enum.GetName(typeof(Volume), val)}");
}
Console.ReadLine(); // Keep the console window open
}
}
Operating Overloading
using System;
class Matrix {
public const int DimSize = 3; // Changed to PascalCase for C# naming conventions
private double[,] m_matrix = new double[DimSize, DimSize];
// Allow callers to initialize
public double this[int x, int y] {
get { return m_matrix[x, y]; }
set { m_matrix[x, y] = value; }
}
// Let user add matrices
public static Matrix operator +(Matrix mat1, Matrix mat2) {
Matrix newMatrix = new Matrix(); // Changed to PascalCase for C# naming conventions
for (int x = 0; x < DimSize; x++) {
for (int y = 0; y < DimSize; y++) {
newMatrix[x, y] = mat1[x, y] + mat2[x, y];
}
}
return newMatrix;
}
// You might want to add a method to display the matrix for testing purposes
public void DisplayMatrix() {
for (int x = 0; x < DimSize; x++) {
for (int y = 0; y < DimSize; y++) {
Console.Write($"{m_matrix[x, y]:F2}\t"); // F2 for formatting to 2 decimal places
}
Console.WriteLine();
}
}
// Example of how to use it (optional, for demonstration)
static void Main(string[] args) {
Matrix matrix1 = new Matrix();
Matrix matrix2 = new Matrix();
// Initialize matrix1
Console.WriteLine("Initializing Matrix 1:");
for (int i = 0; i < DimSize; i++) {
for (int j = 0; j < DimSize; j++) {
matrix1[i, j] = (i + 1) * (j + 1);
}
}
matrix1.DisplayMatrix();
// Initialize matrix2
Console.WriteLine("\nInitializing Matrix 2:");
for (int i = 0; i < DimSize; i++) {
for (int j = 0; j < DimSize; j++) {
matrix2[i, j] = (i + 1) + (j + 1);
}
}
matrix2.DisplayMatrix();
// Add matrices
Console.WriteLine("\nResult of Matrix 1 + Matrix 2:");
Matrix resultMatrix = matrix1 + matrix2;
resultMatrix.DisplayMatrix();
Console.ReadLine(); // Keep console open
}
}
Access Modifiers
private
protected
internal
protected internal
public