Structures , Enumerations and Namespaces

Structures

Value types are defined using the struct keyword instead of class. In most aspects, structures are identical to classes and the characteristics presented in this chapter for classes apply to structures too. However, there are several key differences:

  • Structures do not support inheritance. Although a structure can implement any number of interfaces, it cannot derive from another structure. For this reason, structure members cannot have the protected access modifier. Also, a structured method or property cannot be abstract or virtual.
  • A structure cannot declare a default (parameterless) constructor.
  • Structures can be instantiated without using the new operator.
  • In a structure declaration, fields cannot be initialized unless they are declared const or static.

Enumerations

enum Priority:byte
{
    Low = 10,
    Normal,//11
    Important = 20,
    Urgent//21
}
Priority p = Priority.Normal;
int i = (int)Priority.Normal;
Enum.TryParse("Normal", out Priority p); // p is Normal
Enum.TryParse(typeof(Priority), "normal", true, out object o);
Priority p = (Priority)o;   // p is Normal

Namespaces

namespace chapter_04
{
   namespace demo
   {
      class foo { }
   }
}

//or 

namespace chapter_04.demo
{
   class foo { }
}

There is an implicit namespace that is the root of all namespaces (and contains all namespaces and types that are not declared in a named namespace). This namespace is called global. If you need to include it to specify a fully qualified name, then you must separate it with :: and not with a dot, as in global::System.String. This can be necessary in situations where namespace names collide. Here is an example:

Classes and Objects

Classes

Fields

class Employee
{
   public const int StartId = 100;
   public readonly int EmployeeId;
   public string       FirstName;
   public string       LastName;
   public Employee(int id)
   {
      EmployeeId = id;
   }
}
Employee obj = new Employee(1);
obj.FirstName = "John";
obj.LastName = "Doe";

Methods

class Employee
{
    public int    EmployeeId;
    public string FirstName;
    public string LastName;
    public string GetEmployeeName()
    {
        return $"{FirstName} {LastName}";
    }
    public string GetEmployeeName() => $"{FirstName} {LastName}";
}
  • An access modifier: This specifies the visibility of the method. This is optional and private by default.
  • Modifiers such as virtualabstractsealed, or static
  • A return type: This could be void if the method does not return any value.
  • A name: This must be a valid identifier.
  • Zero, one, or more parameters: These are specified with a type, name, and optionally, the refin, or out specifier.
class Employee
{
    public int EmployeeId;
    public string FirstName;
    public string LastName;
    public Employee(int employeeId, 
                    string firstName, string lastName)
    {
        EmployeeId = employeeId;
        FirstName = firstName;
        LastName = lastName;
    }
    public string GetEmployeeName() => 
           $"{FirstName} {LastName}";   
}

Employee obj = new Employee(1, "John", "Doe");
//Considering the Employee class without a user-defined constructor
Employee obj = new Employee()
{
    EmployeeId = 1,
    FirstName = "John",
    LastName = "Doe"
};

Properties

class Employee
{
   private int employeeId;
   private string firstName;
   private string lastName;
   public int EmployeeId
   {
      get { return employeeId; }
      set { employeeId = value; }
   }
   public string FirstName
   {
      get { return firstName; }
      set { firstName = value; }
   }
   public string LastName
   {
      get { return lastName; }
      set { lastName = value; }
   }
   public string val
   {
      get{}
      private set {}
   }
}
class Employee
{
    public int EmployeeId { get; private set; }
    public string FirstName { get; private set; }
    public string LastName { get; private set; }
    public Employee(int id, string firstName, string lastName)
    {
        EmployeeId = id;
        FirstName = firstName;
        LastName = lastName;
    }
}
class Employee
{
   public int EmployeeId { get; set; } = 1;//initialized with 1
   public string FirstName { get; set; }
   public string LastName { get; set; }
}

Indexers

class ProjectRoles
{
    readonly Dictionary<int, string> roles = 
        new Dictionary<int, string>();
    public string this[int projectId]
    {
        get
        {
            if (!roles.TryGetValue(projectId, out string role))
                throw new Exception("Project ID not found!");
            return role;
        }
        set
        {
            roles[projectId] = value;
        }
    }
}

this keyword

class Employee
{
    public int EmployeeID;
    public string FirstName;
    public string LastName;
    public Employee(int EmployeeID, 
                    string FirstName, string LastName)
    {
       this.EmployeeID = EmployeeID;
       this.FirstName = FirstName;
       this.LastName = LastName;
    }
}

Static members

class Employee
{
    private static int id = 1;
    public int EmployeeId { get; private set; }
    public string FirstName { get; private set; }
    public string LastName { get; private set; }
    private Employee(int id, string firstName, string lastName)
    {
        EmployeeId = id;
        FirstName = firstName;
        LastName = lastName;
    }
    public static Employee Create(string firstName, 
                                  string lastName)
    {
        return new Employee(id++, firstName, lastName);
    }
}

Static classes

static class cannot be instantiated. Since we cannot create instances of a static class

static class MassConverters
{
    public static double PoundToKg(double pounds)
    {
        return pounds * 0.45359237;
    }
    public static double KgToPound(double kgs)
    {
        return kgs * 2.20462262185;
    }
}
var lbs = MassConverters.KgToPound(42.5);
var kgs = MassConverters.PoundToKg(180);

static constructor

  • In a static class when the first static member of the class is accessed for the first time
  • In a non-static class when the class is instantiated for the first time

ref, in, and out parameters

ref keyword allows us to create a call-by-reference mechanism rather than a call-by-value mechanism. A ref keyword is specified when we declare and invoke the method.

class Program
{
    static void Swap(ref int a, ref int b)
    {
        int temp = a;
        a = b;
        b = temp;
    }
    static void Main(string[] args)
    {
        int num1 = 10;
        int num2 = 20;
        Console.WriteLine($"Before swapping: num1={num1}, num2={num2}");
        Swap(ref num1, ref num2);
        Console.WriteLine($"After swapping:  num1={num1}, num2={num2}");
    }
}
class Project
{
    Employee owner;
    public string Name { get; private set; }
    public Project(string name, Employee owner)
    {
        Name = name;
        this.owner = owner;
    }
    public ref Employee GetOwner()
    {
        return ref owner;
    }
    public override string ToString() => 
      $"{Name} (Owner={owner.FirstName} {owner.LastName})";
}
  • It is not possible to return a reference to a local variable.
  • It is not possible to return a reference to this.
  • It is possible to return references to class fields but also to properties without a set accessor.
  • It is possible to return a reference to ref/in/out parameters.
  • Returning by reference breaks the encapsulation because the caller gets full access to the state, or parts of the state, of an object.

The in keyword is very similar to the ref keyword. It causes an argument to be passed by reference. However, the key difference is that an in argument cannot be modified by the called method. An in parameter is basically a readonly ref parameter.

static void DontTouch(in int value, in string text)
{
    value = 42;   // error
    ++value;      // error
    text = null;  // error
}
int i = 0;
string s = "hello";
DontTouch(i, s);

The out keyword is similar to the ref keyword. The difference is that a variable passed as an out argument does not have to be initialized before the method called, but the method taking an out parameter must assign a value to it before returning.

Methods with a variable number of arguments

static bool Any(params bool [] values)
{
    foreach (bool v in values)
        if (v) return true;
    return false;
}
static bool All(params bool[] values)
{
    if (values.Length == 0) return false;
    foreach (bool v in values)
        if (!v) return false;
    return true;
}

Named arguments

struct Point
{
    public int X { get; }
    public int Y { get; }
    public Point(int x = 0, int y = 0)
    {
        X = x;
        Y = y;
    }
}

Point p1 = new Point(x: 1, y: 2); // x = 1, y = 2
Point p2 = new Point(1, y: 2);    // x = 1, y = 2
Point p3 = new Point(x: 1, 2);    // x = 1, y = 2
Point p4 = new Point(y: 2);       // x = 0, y = 2
Point p5 = new Point(x: 1);       // x = 1, y = 0

Access modifiers

  • public: A public field can be accessed by any part of the code in the same assembly or in another assembly.
  • protected: A protected type or member can be accessed only in the current class and in a derived class.
  • internal: An internal type or member is accessible only within the current assembly.
  • protected internal: This is a combination of protected and internal access levels. A protected internal type or member is accessible in the current assembly or in a derived class.
  • private: A private type or member can be accessed only inside the class or struct. This is the least-accessible level defined in C#.
  • private protected: This is a combination of private and protected access levels. A private protected type or type member is accessible by code in the same class, or in a derived class, but only within the same assembly.

Partial classes

partial class Employee
{
    partial void Promote();
}
partial class Employee
{
    public int EmployeeId { get; set; }
}
partial class Employee
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    partial void Promote()
    {
        Console.WriteLine("Employee promoted!");
    }
}

control statements

switch

is only used with values types

switch (expression)
{
  case value1:
    statement 1;
    break;
  case value2:
    statement 2;
    statement 3;
    break;
  default:
    statement 4;
    break;
}

try,catch,finally

try
            {
                Console.WriteLine("nice started");
                int l = 12;
                throw new Exception("there are problem");
            }
            catch(Exception e)
            {
                Console.WriteLine(e.Message);
            }
            finally // called at the end and don't care if is their exception or not
            {
                Console.WriteLine("nice finished");
            }

foreach

string[] languages = { "Java", "C#", "Python", "C++", "JavaScript" };
foreach (string lang in languages)
{
    Console.WriteLine(lang);
}
Span<int> arr = stackalloc int[]{ 1, 1, 2, 3, 5, 8 };
foreach(ref int n in arr)
{
    n *= 2;
}
foreach(ref readonly var n in arr)
{
    Console.WriteLine(n);
}

goto

for (int i = 0; i <= 10; i++)
{
    Console.WriteLine(i);
    if (i == 5)
    {
        goto printmessage;
    }
}
printmessage:
    Console.WriteLine("The goto statement is executed");

yield return and yield break

IEnumerable<int> GetNumbers()
{
    for (int i = 1; i <= 100; ++i)
    {
        Thread.Sleep(1000);
        Console.WriteLine($"Produced: {i}");
        yield return i;
    }
}
foreach(var i in GetNumbers().Take(5))
{
    Console.WriteLine($"Consumed: {i}");
}

disposing with using block

using System;

class DisposableDemoClass : IDisposable
{
	public void Dispose()
	{
		Console.WriteLine("Dispose called!");
	}
}

class Hello 
{
    static void Main() 
    {
		using(var disp = new DisposableDemoClass())
		{
		}
    }
}