Skip to content

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 with Win32 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