Jump to content

C# Programming/Classes

From Wikibooks, open books for an open world

As in other object-oriented programming languages, the functionality of a C# program is implemented in one or more classes. The methods and properties of a class contain the code that defines how the class behaves.

C# classes support information hiding by encapsulating functionality in properties and methods and by enabling several types of polymorphism, including subtyping polymorphism via inheritance and parametric polymorphism via generics.

Several types of C# classes can be defined, including instance classes (standard classes that can be instantiated), static classes, and structures.

Classes are defined using the keyword class followed by an identifier to name the class. Instances of the class can then be created with the new keyword followed by the name of the class.

The code below defines a class called Employee with properties Name and Age and with empty methods GetPayCheck() and Work(). It also defines a Sample class that instantiates and uses the Employee class:

public class Employee
{
    private int _Age;
    private string _Name;

    public int Age
    {
        get { return _Age; }
        set { _Age = value; }
    }

    public string Name
    {
        get { return _Name; }
        set { _Name = value; }
    }

    public void GetPayCheck()
    {
    }

    public void Work()
    {
    }
}

public class Sample
{
    public static void Main()
    {
        Employee marissa = new Employee();

        marissa.Work();
        marissa.GetPayCheck();
    }
}

Methods

[edit | edit source]

C# methods are class members containing code. They may have a return value and a list of parameters, as well as a generic type declaration. Like fields, methods can be static (associated with and accessed through the class) or instance (associated with and accessed through an object instance of the class methods as well as a generic type declaration.

From C# 4.0 onwards, it is possible for a method to have optional parameters with default values, as users of C++ already know. For example, the method

void Increment(ref int x, int dx = 1)

can be called with one parameter only, as the second parameter, dx, is initialised to a default value.[1]

Constructors of classes

[edit | edit source]

A class's constructors control its initialization. A constructor's code executes to initialize an instance of the class when a program requests a new object of the class's type. Constructors often set properties of their classes, but they are not restricted to doing so.

Like other methods, a constructor can have parameters. To create an object using a constructor with parameters, the new command accepts parameters. The code below defines and then instantiates multiple objects of the Employee class, once using the constructor without parameters and once using the version with a parameter:

public class Employee
{
    public Employee()
    {
        System.Console.WriteLine("Constructed without parameters");
    }

    public Employee(string strText)
    {
        System.Console.WriteLine(strText);
    }
}

public class Sample
{
    public static void Main()
    {
        System.Console.WriteLine("Start");
        Employee Alfred = new Employee();
        Employee Billy  = new Employee("Parameter for construction");
        System.Console.WriteLine("End");
    }

Output:

Start
Constructed without parameters
Parameter for construction
End

Constructors can call each other:

public class Employee
{
    public Employee(string strText, int iNumber)
    {
        ...
    }
    
    public Employee(string strText)
        : this(strText, 1234) // calls the above constructor with user-specified text and the default number
    { }
    
    public Employee()
        : this("default text") // calls the above constructor with the default text
    { }
}

Finalizers (Destructors)

[edit | edit source]

The opposite of constructors, finalizers define the final behavior of an object and execute when the object is no longer in use. Although they are often used in C++ to free resources reserved by an object, they are less frequently used in C# due to the .NET Framework Garbage Collector. An object's finalizer, which takes no parameters, is called sometime after an object is no longer referenced, but the complexities of garbage collection make the specific timing of finalizers uncertain.

public class Employee
{
    public Employee(string strText)
    {
        System.Console.WriteLine(strText);
    }

    ~Employee()
    {
        System.Console.WriteLine("Finalized!");
    }

    public static void Main()
    {
        Employee marissa = new Employee("Constructed!");

        marissa = null;
    }
}

Output:

Constructed!
Finalized!

Properties

[edit | edit source]

C# properties are class members that expose functionality of methods using the syntax of fields. They simplify the syntax of calling traditional get and set methods (a.k.a. accessor methods). Like methods, they can be static or instance.

Properties are defined in the following way:

public class MyClass
{
    private int m_iField = 3; // Sets integerField with a default value of 3

    public int IntegerField
    {
        get
        {
            return m_iField;  // get returns the field you specify when this property is assigned
        }
        set
        {
            m_iField = value; // set assigns the value assigned to the property of the field you specify
        }
    }
}

An even shorter way for getter/setter methods are accessors that do both in one line:

class Culture
{
    public int TalkedCountries { get; set; }
    public string Language { get; set; }
}

class InterculturalDialogue
{
    Culture culture;

    culture.Language = "Italian";  // ==> culture.SetLanguage("Italian");

    string strThisLanguage = culture.Language; // ==> ... = culture.GetLanguage();
}

The code is equivalent to a GetLanguage and SetLanguage method definition, but without having to define these methods. The user can directly access the member, if it is not private, of course.

The C# keyword value contains the value assigned to the property. After a property is defined it can be used like a variable. If you were to write some additional code in the get and set portions of the property it would work like a method and allow you to manipulate the data before it is read or written to the variable.

public class MyProgram
{
    MyClass myClass = new MyClass;

    Console.WriteLine(myClass.IntegerField); // Writes 3 to the command line.
    myClass.IntegerField = 7; // Indirectly assigns 7 to the field myClass.m_iField     
}

Using properties in this way provides a clean, easy to use mechanism for protecting data.

Indexers

[edit | edit source]

C# indexers are class members that define the behavior of the array access operation (e.g. list[0] to access the first element of list even when list is not an array).

To create an indexer, use the this keyword as in the following example:

public string this[string strKey]
{
    get { return coll[strKey]; }
    set { coll[strKey] = value; }
}

This code will create a string indexer that returns a string value. For example, if the class was EmployeeCollection, you could write code similar to the following:

EmployeeCollection e = new EmployeeCollection();
.
.
.
string s = e["Jones"];
e["Smith"] = "xxx";

Events

[edit | edit source]

C# events are class members that expose notifications to clients of the class. Events are only fired and never assigned.

using System;

// Note: You need to know some about delegate, properties and methods to understand this sample
namespace EventSample
{
    /// <summary>
    /// This delegate defines the signature of the appropriate method
    /// </summary>
    public delegate void ContractHandler(Employee sender);

    /// <summary>
    ///     Employee class
    /// </summary>
    public class Employee
    {
        /// <summary>
        ///     Field for the info whether or not the Employee is engaged
        /// </summary>
        private bool bIsEngaged = false;
        /// <summary>
        ///     Age of the employee
        /// </summary>
        private int iAge = -1;
        /// <summary>
        ///     Name of the employee
        /// </summary>
        private String strName = null;

        /// <summary>
        /// *** The our event *** 
        /// Is a collection of methods that will be called when it fires
        /// </summary>
        public event ContractHandler Engaged;

        /// <summary>
        ///     Standard constructor
        /// </summary>
        public Employee()
        {
            // Here, we are adding a new method with appropriate signature (defined by delegate)
            // note: when an event has no method and it was fired, it causes an exception!
            //       for all effects when programming with events, assign one private method to event
            //       or simply do a verification before firing it! --> if (event != null)
            this.Engaged += new ContractHandler(this.OnEngaged);
        }

        /// <summary>
        ///     Event handler for the "engaged" event
        /// </summary>
        /// <param name="sender">
        ///     Sender object
        /// </param>
        private void OnEngaged(Employee sender)
        {
            Console.WriteLine("private void OnEngaged was called! this employee is engaged now!");
        }

        /// <summary>
        ///     Accessor for the employee name
        /// </summary>
        public string Name
        {
            get
            {
                return strName;
            }

            set
            {
                strName = value;
            }
        }

        /// <summary>
        ///     Accessor for the employee age
        /// </summary>
        public int Age
        {
            get
            {
                return m_iAge;
            }

            set
            {
                m_iAge = value;
            }
        }

        /// <summary>
        ///     Accessor for the information about employee engagement
        /// </summary>
        public bool IsEngaged
        {
            get
            {
                return bIsEngaged;
            }

            set
            {
                if (bIsEngaged == false && value == true)
                {
                    // here we fires event (call all the methods that it have)
                    // all times when IsEngaged is false and set to true;
                    Engaged(this);
                }

                bIsEngaged = value;
            }
        }
    }

    /// <summary>
    ///     Class for the entry point
    /// </summary>
    public class EntryPointClass
    {
        static void Main(string[] a_strArgs)
        {
            Employee simpleEmployee = new Employee();

            simpleEmployee.Age = 18;
            simpleEmployee.Name = "Samanta Rock";
            
            // Here...
            // This is saying when the event fires, the method added to event are called too.
            // note that we cannot use =
            // is only += to add methods to event or -= do retire an event
            simpleEmployee.Engaged += new ContractHandler(SimpleEmployee_Engaged);
        
            // make attention here...
            // when I assign true to this property, 
            // the event Engaged will be called
            // when event is called, all method that it have, are called!
            simpleEmployee.IsEngaged = true;

            Console.ReadLine();

            return;
        }

        /// <summary>
        ///     Event handler for the registered "engaged" event
        /// </summary>
        /// <param name="sender">
        ///     Event sender
        /// </param>
        static void SimpleEmployee_Engaged(Employee sender)
        {
            Console.WriteLine("The employee {0} is happy!", sender.Name);
        }
    }
}

See also here for details.

Operator overloading

[edit | edit source]

C# operator definitions are class members that define or redefine the behavior of basic C# operators (called implicitly or explicitly) on instances of the class:

public class Complex
{
    private double m_dReal, m_dImaginary;
    
    public double Real
    {
        get { return m_dReal; }
        set { m_dReal = value; }
    }
    
    public double Imaginary
    {
        get { return m_dImaginary; }
        set { m_dImaginary = value; }
    }
    
    // binary operator overloading
    public static Complex operator +(Complex c1, Complex c2)
    {
        return new Complex() { Real = c1.Real + c2.Real, Imaginary = c1.Imaginary + c2.Imaginary };
    }
    
    // unary operator overloading
    public static Complex operator -(Complex c)
    {
        return new Complex() { Real = -c.Real, Imaginary = -c.Imaginary };
    }
    
    // cast operator overloading (both implicit and explicit)
    public static implicit operator double(Complex c)
    {
        // return the modulus - sqrt(x^2 + y^2)
        return Math.Sqrt(Math.Pow(c.Real, 2) + Math.Pow(c.Imaginary, 2));
    }
    
    public static explicit operator string(Complex c)
    {
        // we should be overloading the ToString() method, but this is just a demonstration
        return c.Real.ToString() + " + " + c.Imaginary.ToString() + "i";
    }
}

public class StaticDemo
{
    public static void Main()
    {
        Complex number1 = new Complex() { Real = 1, Imaginary = 2 };
        Complex number2 = new Complex() { Real = 4, Imaginary = 10 };
        Complex number3 = number1 + number2; // number3 now has Real = 5, Imaginary = 12
        
        number3 = -number3; // number3 now has Real = -5, Imaginary = -12
        double testNumber = number3; // testNumber will be set to the absolute value of number3
        Console.WriteLine((string)number3); // This will print "-5 + -12i".
        // The cast to string was needed because that was an explicit cast operator.
    }
}

Structures

[edit | edit source]

Structures, or structs, are defined with the struct keyword followed by an identifier to name the structure. They are similar to classes, but have subtle differences. Structs are used as lightweight versions of classes that can help reduce memory management efforts when working with small data structures. In most situations, however, using a standard class is a better choice.

The principal difference between structs and classes is that instances of structs are values whereas instances of classes are references. Thus when you pass a struct to a function by value you get a copy of the object so changes to it are not reflected in the original because there are now two distinct objects but if you pass an instance of a class by reference then there is only one instance.

The Employee structure below declares a public and a private field. Access to the private field is granted through the public property Name:

struct Employee
{
    public int m_iAge;
    private string m_strName;

    public string Name
    {
        get { return m_strName; }
        set { m_strName = value; }
    }
}

Since C# 2.0, is possible to have arrays inside structures, but only in unsafe contexts:

struct data
{
    int header;
    fixed int values[10];
}

The array is accessed using pointer arithmetic. Values are treat arrayed values as if they were C-style arrays using indexing, etc.

Structure constructors

[edit | edit source]

Structures need constructors - or better to say initialisers, as they do not construct but just initialise the memory[2] - so that their contents are not left uninitialised. Therefore, constructors without parametres are not allowed.

Structure variables can be assigned one to another if and only if the structure variable on the right side of the assignment are all initialised.[3]

struct Timestamp
{
    private ushort m_usYear;
    private ushort m_usMonth;
    private ushort m_usDayOfMonth;
    private ushort m_usHour;
    private ushort m_usMinute;
    private ushort m_usSecond;

    public Timestamp(ushort usYear,
        ushort usMonth,
        ushort usDay,
        ushort usHour,
        ushort usMinute,
        ushort usSecond)
    {
        m_usYear = usYear - 1900;
        m_usMonth = usMonth;
        m_usDay = usDay;
        m_usHour = usHour;
        m_usMinute = usMinute;
        m_usSecond = usSecond;
    }
}

Static classes

[edit | edit source]

Static classes are commonly used to implement a Singleton Pattern. All of the methods, properties, and fields of a static class are also static (like the WriteLine() method of the System.Console class) and can thus be used without instantiating the static class:

public static class Writer
{
    public static void Write()
    {
        System.Console.WriteLine("Text");
    }
}

public class Sample
{
    public static void Main()
    {
        Writer.Write();
    }
}

References

[edit | edit source]
  1. [[[w:C_Sharp_syntax#Optional_parameters|Optional parameters]] "Optional parameters"]. Retrieved 2018-02-01. {{cite web}}: Check |url= value (help)
  2. Greg Beech (2008-06-16). "Structure constructors". http://social.msdn.microsoft.com/: MSDN. Retrieved 2012-04-12. Because structures are simply an inline area of memory, they cannot be null, and so the CLR has to be able to ensure that the area of memory is totally initialized rather than being partly garbage. For this reason, you'll often hear the 'constructors' on structures called (arguably more correctly) 'initializers' because the don't construct an object they just initialize an area of memory. {{cite web}}: External link in |location= (help)
  3. John Sharp. "Microsoft® Visual C#® 2005 Step by Step / Copying Structure Variables". http://books.google.at/: Google Books. Retrieved 2012-04-12. You're allowed to initialize or assign one struct variable to another struct variable, but only if the struct variable on the right side is completely initialized (that is, if all its fields are initialized). {{cite web}}: External link in |location= (help)