Keri sisuni

Staatiline skoop

Seni oleme tegelenud klassidega sel teel, et enne nende kasutamist loome uue instantsi new abil. Loodud objektil on oma muutujad, mis on sõltumatud kõikidest teistest sama klassi põhjal loodud objetide omadest. Selle kohta ütleme, et kasutame klassi instantsi skoopi (instance scope). Lisaks sellele on klassidel olemas staatiline skoop (static scope) ning see on jagatud klassi kõikide instantside vahel. Staatilises skoobis olevate liikmete kasutamiseks ei pea me klassist uut instantsi looma.

public class StaticDemo
{
    // staatiline klassi muutuja
    public static string _staticVariable;

    // staatiline konstruktor
    static StaticDemo()
    {
        _staticVariable = "I am static variable";

        StaticProperty = "I am static property";
    }

    // staatiline omadus
    public static string StaticProperty { get; set; }

    // staatiline meetod
    public static string StaticMethod()
    {
        return "Static method was called";
    }
}

Console.WriteLine(StaticDemo._staticVariable);
Console.WriteLine(StaticDemo.StaticProperty);
Console.WriteLine(StaticDemo.StaticMethod());
using System;

namespace OOP
{
    public class StaticDemo
    {
        public static string _staticVariable;

        static StaticDemo()
        {
            _staticVariable = "I am static variable";

            StaticProperty = "I am static property";
        }

        public static string StaticProperty { get; set; }

        public static string StaticMethod()
        {
            return "Static method was called";
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine(StaticDemo._staticVariable);
            Console.WriteLine(StaticDemo.StaticProperty);
            Console.WriteLine(StaticDemo.StaticMethod());
        }
    }
}

Koodi käivitamisel saame tulemuseks

I am static variable
I am static property
Static method was called

Klassi staatilist skoopi nimetatakse ka jagatud skoobiks (shared scope), sest kõik instantsid pääsevad staatilistele liikmetele juurde ning staatilist skoopi on klassil alati üks. See tähendab seda, et staatilisse skoopi saame koondada kõik antud klassiga seotud toimingud ja andmed, mis on instantsideülesed.

Staatiline konstruktor

Klassil saab olla üks staatiline konstruktor. Parameetreid sellel pole, sest me ei saa kuidagi staatilist skoopi ise luua. Staatiline konstruktor ei käivitu koheselt programmi alguses, vaid siis kui klassi poole esimest korda koodis pöördutakse.

class Class1
{
    static Class1()
    {
        Console.WriteLine("Class1 static constructor");
    }
}

class Class2
{
    static Class2()
    {
        Console.WriteLine("Class2 static constructor");
    }

    public static string StaticProperty { get; set; }
}

class Class3
{
    static Class3()
    {
        Console.WriteLine("Class3 static constructor");
    }
}

var c1 = new Class1();
var s1 = Class2.StaticProperty;
using System;

namespace OOP
{
    class Program
    {
        static void Main()
        {
            var c1 = new Class1();
            var s1 = Class2.StaticProperty;
        }
    }

    class Class1
    {
        static Class1()
        {
            Console.WriteLine("Class1 static constructor");
        }
    }

    class Class2
    {
        static Class2()
        {
            Console.WriteLine("Class2 static constructor");
        }

        public static string StaticProperty { get; set; }
    }

    class Class3
    {
        static Class3()
        {
            Console.WriteLine("Class3 static constructor");
        }
    }
}

Programmi käivitamisel saame sellise väljundi:

Class1 static constructor
Class2 static constructor

Staatilise konstruktori käivitamine

Staatiline konstruktor käivitatakse kohe kui püüame klassi ükskõik millisel moel kasutada. Enne kui saame midagi teha instantsi või staatilises skoobis, käivitatakse klassi staatiline konstruktor, mis staatilise skoobi initsialiseerib.

Staatilised omadused

Staatilise omaduse näitena vaatame klassi, mis oskab arvet pidada oma instanside arvu üle. Sellist funktsionaalsust kasutatakse ka praktikas, kuid oluliselt keerukamal kujul.

public class InstanceCounterDemo
{
    public static int InstanceCount { get; private set; }

    public InstanceCounterDemo()
    {
        InstanceCount++;
    }
}

var instanceCounter1 = new InstanceCounterDemo();
var instanceCounter2 = new InstanceCounterDemo();

Console.WriteLine(InstanceCounterDemo.InstanceCount);

var instanceCounter3 = new InstanceCounterDemo();
var instanceCounter4 = new InstanceCounterDemo();

Console.WriteLine(InstanceCounterDemo.InstanceCount);
using System;

namespace OOP
{
    public class InstanceCounterDemo
    {
        public static int InstanceCount { get; private set; }

        public InstanceCounterDemo()
        {
            InstanceCount++;
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            var instanceCounter1 = new InstanceCounterDemo();
            var instanceCounter2 = new InstanceCounterDemo();

            Console.WriteLine(InstanceCounterDemo.InstanceCount);

            var instanceCounter3 = new InstanceCounterDemo();
            var instanceCounter4 = new InstanceCounterDemo();

            Console.WriteLine(InstanceCounterDemo.InstanceCount);
        }
    }
}

Väljundiks saame

2
4

Staatilised meetodid

Staatilised meetodid on sarnaselt kõigele muule staatilisele instantside ülesed, kuid võib öelda ka, et instantside välised. Vaatleme Customer klassi, millel on staatiline meetod Deserialize(), mille abil saame XML-ist teha antud klassi instantsi.

public class Customer
{
    public int Id { get; set; }
    public string Name { get; set;}
    public string Address { get; set; }
    public string Phone { get; set; }
    public string Email { get; set; }

    public static Customer Deserialize(string xml)
    {
        var serializer = new XmlSerializer(typeof(Customer), new XmlRootAttribute("Customer"));

        using (var reader = new StringReader(xml))
        {
            return (Customer)serializer.Deserialize(reader);
        }
    }
}

var xml = @"
    <Customer>
        <Id>1</Id>
        <Name>John Smith</Name>
        <Address></Address>
        <Phone>+334233635232</Phone>
        <Email>john.smith@example.com</Email>
    </Customer>
";

var customer = Customer.Deserialize(xml);

Console.WriteLine("Id: " + customer.Id);
Console.WriteLine("Name: " + customer.Name);
Console.WriteLine("Address: " + customer.Address);
Console.WriteLine("Phone: " + customer.Phone);
Console.WriteLine("Email: " + customer.Email);
using System;
using System.IO;
using System.Xml.Serialization;

namespace OOP
{
    public class Customer
    {
        public int Id { get; set; }
        public string Name { get; set;}
        public string Address { get; set; }
        public string Phone { get; set; }
        public string Email { get; set; }

        public static Customer Deserialize(string xml)
        {
            var serializer = new XmlSerializer(typeof(Customer), new XmlRootAttribute("Customer"));

            using (var reader = new StringReader(xml))
            {
                return (Customer)serializer.Deserialize(reader);
            }
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            var xml = @"
                <Customer>
                   <Id>1</Id>
                   <Name>John Smith</Name>
                   <Address></Address>
                   <Phone>+334233635232</Phone>
                   <Email>john.smith@example.com</Email>
                </Customer>
            ";

            var customer = Customer.Deserialize(xml);

            Console.WriteLine("Id: " + customer.Id);
            Console.WriteLine("Name: " + customer.Name);
            Console.WriteLine("Address: " + customer.Address);
            Console.WriteLine("Phone: " + customer.Phone);
            Console.WriteLine("Email: " + customer.Email);
        }
    }
}

Eeltoodud kood väljastab kõik antud kliendi andmed:

Id: 1
Name: John Smith
Address:
Phone: +334233635232
Email: john.smith@example.com

Staatilised klassid

C# toetab ka staatilisi klasse. Need on klassid, millel instantsi skoop puudub ning millel on ainult staatilised liikmed. Staatilised on tavaliselt klassid, milles on laiendusmeetodid või siis igasugused abistavad funktsioonid.

Näitena vaatame utiliitklassi, mis pakub mõningaid krüpteerimise funktsioone.

public static class CryptoUtils
{
    public static string CreateMD5(string input)
    {
        using (var md5 = MD5.Create())
        {
            byte[] inputBytes = Encoding.UTF8.GetBytes(input);
            byte[] hashBytes = md5.ComputeHash(inputBytes);

            var builder = new StringBuilder();
            for (int i = 0; i < hashBytes.Length; i++)
            {
                builder.Append(hashBytes[i].ToString("X2"));
            }

            return builder.ToString();
        }
    }

    public static string CreateSha256(string input)
    {
        using (var sha256 = SHA256.Create())
        {
            byte[] inputBytes = Encoding.UTF8.GetBytes(input);
            byte[] hashBytes = sha256.ComputeHash(inputBytes);

            var builder = new StringBuilder();
            for (int i = 0; i < hashBytes.Length; i++)
            {
                builder.Append(hashBytes[i].ToString("x2"));
            }

            return builder.ToString();
        }
    }
}

var valueToHash = "Big Bad Wolf";

Console.WriteLine(CryptoUtils.CreateMD5(valueToHash));
Console.WriteLine(CryptoUtils.CreateSha256(valueToHash));
using System;
using System.Security.Cryptography;
using System.Text;

namespace OOP
{
    public static class CryptoUtils
    {
        public static string CreateMD5(string input)
        {
            using (var md5 = MD5.Create())
            {
                byte[] inputBytes = Encoding.UTF8.GetBytes(input);
                byte[] hashBytes = md5.ComputeHash(inputBytes);

                var builder = new StringBuilder();
                for (int i = 0; i < hashBytes.Length; i++)
                {
                    builder.Append(hashBytes[i].ToString("X2"));
                }

                return builder.ToString();
            }
        }

        public static string CreateSha256(string input)
        {
            using (var sha256 = SHA256.Create())
            {
                byte[] inputBytes = Encoding.UTF8.GetBytes(input);
                byte[] hashBytes = sha256.ComputeHash(inputBytes);

                var builder = new StringBuilder();
                for (int i = 0; i < hashBytes.Length; i++)
                {
                    builder.Append(hashBytes[i].ToString("x2"));
                }

                return builder.ToString();
            }
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            var valueToHash = "Big Bad Wolf";

            Console.WriteLine(CryptoUtils.CreateMD5(valueToHash));
            Console.WriteLine(CryptoUtils.CreateSha256(valueToHash));
        }
    }
}

Tulemuseks saame sellised MD5 ja SHA256 räsikoodid:

9D694A75C5407EF92B46942C3DC01F2C
0d523ed9876e4737fcd42d4db0700c99d84533011076ad704133ea58c8524d82

Laiendusmeetodid

Staatiliste klasside juures sai mainitud laiendusmeetode (extension method). Need on meetodid, mis näiliselt lisanduvad mõnele olemasolevale klassile, kuid tehniliselt pole osa nendest. Kõige populaarsema laiendusmeetodite kogumi moodustavad LINQ-meetodid.

Kirjutame alustuseks paar lihtsat laiendusmeetodi stringide jaoks. Pane tähele, et laiendusmeetodi esimene parameeter määrab ära, millise tüübi jaoks see laiendusmeetod on mõeldud. Lisaks sellele tuleb antud parameetri ette kirjutada märksõna this.

public static class StringExtensions
{
    // Asenda stringis reavahetus HTML-i reavahetusega
    public static string Nl2Br(this string s)
    {
        return s.Replace("\r\n", "<br />")
                .Replace("\n", "<br />");
    }

    // Korda antud string n korda
    public static string Repeat(this string s, int n)
    {
        return new StringBuilder(s.Length * n)
                        .AppendJoin(s, new string[n+1])
                        .ToString();
    }
}

var stringWithLineBreaks = "First\r\nSecond\nThird";    
var stringToRepeat = "Abc";

Console.WriteLine(stringWithLineBreaks.Nl2Br());
Console.WriteLine(stringToRepeat.Repeat(3));
using System;
using System.Text;

namespace OOP
{
    public static class StringExtensions
    {
        // Asenda stringis reavahetus HTML-i reavahetusega
        public static string Nl2Br(this string s)
        {
            return s.Replace("\r\n", "<br />")
                    .Replace("\n", "<br />");
        }

        // Korda antud string n korda
        public static string Repeat(this string s, int n)
        {
            return new StringBuilder(s.Length * n)
                            .AppendJoin(s, new string[n + 1])
                            .ToString();
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            var stringWithLineBreaks = "First\r\nSecond\nThird";
            var stringToRepeat = "Abc";

            Console.WriteLine(stringWithLineBreaks.Nl2Br());
            Console.WriteLine(stringToRepeat.Repeat(3));
        }
    }
}

Tulemuseks saame:

First<br />Second<br />Third
AbcAbcAbc

Ülaltoodud CryptoUtils klassi meetodid võime viia üle laiendusmeetoditele ja lisada StringExtensions klassile.

public static string ToMD5(this string input)
{
    using (var md5 = MD5.Create())
    {
        byte[] inputBytes = Encoding.UTF8.GetBytes(input);
        byte[] hashBytes = md5.ComputeHash(inputBytes);

        var builder = new StringBuilder();
        for (int i = 0; i < hashBytes.Length; i++)
        {
            builder.Append(hashBytes[i].ToString("X2"));
        }

        return builder.ToString();
    }
}

public static string ToSha256(this string input)
{
    using (var sha256 = SHA256.Create())
    {
        byte[] inputBytes = Encoding.UTF8.GetBytes(input);
        byte[] hashBytes = sha256.ComputeHash(inputBytes);

        var builder = new StringBuilder();
        for (int i = 0; i < hashBytes.Length; i++)
        {
            builder.Append(hashBytes[i].ToString("x2"));
        }

        return builder.ToString();
    }
}

var stringWithLineBreaks = "First\r\nSecond\nThird";
var stringToRepeat = "Abc";
var valueToHash = "Big Bad Wolf";

Console.WriteLine(stringWithLineBreaks.Nl2Br());
Console.WriteLine(stringToRepeat.Repeat(3));
Console.WriteLine(valueToHash.ToMD5());
Console.WriteLine(valueToHash.ToSha256());
using System;
using System.Security.Cryptography;
using System.Text;

namespace OOP
{
    public static class StringExtensions
    {
        // Asenda stringis reavahetus HTML-i reavahetusega
        public static string Nl2Br(this string s)
        {
            return s.Replace("\r\n", "<br />")
                    .Replace("\n", "<br />");
        }

        // Korda antud string n korda
        public static string Repeat(this string s, int n)
        {
            return new StringBuilder(s.Length * n)
                            .AppendJoin(s, new string[n + 1])
                            .ToString();
        }

        public static string ToMD5(this string input)
        {
            using (var md5 = MD5.Create())
            {
                byte[] inputBytes = Encoding.UTF8.GetBytes(input);
                byte[] hashBytes = md5.ComputeHash(inputBytes);

                var builder = new StringBuilder();
                for (int i = 0; i < hashBytes.Length; i++)
                {
                    builder.Append(hashBytes[i].ToString("X2"));
                }

                return builder.ToString();
            }
        }

        public static string ToSha256(this string input)
        {
            using (var sha256 = SHA256.Create())
            {
                byte[] inputBytes = Encoding.UTF8.GetBytes(input);
                byte[] hashBytes = sha256.ComputeHash(inputBytes);

                var builder = new StringBuilder();
                for (int i = 0; i < hashBytes.Length; i++)
                {
                    builder.Append(hashBytes[i].ToString("x2"));
                }

                return builder.ToString();
            }
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            var stringWithLineBreaks = "First\r\nSecond\nThird";
            var stringToRepeat = "Abc";
            var valueToHash = "Big Bad Wolf";

            Console.WriteLine(stringWithLineBreaks.Nl2Br());
            Console.WriteLine(stringToRepeat.Repeat(3));
            Console.WriteLine(valueToHash.ToMD5());
            Console.WriteLine(valueToHash.ToSha256());
        }
    }
}

Proovimine annab tulemuseks sellise väljundi:

First<br />Second<br />Third
AbcAbcAbc
9D694A75C5407EF92B46942C3DC01F2C
0d523ed9876e4737fcd42d4db0700c99d84533011076ad704133ea58c8524d82

Süntaksilt on laiendusmeetodid lühemad võrreldes sellega kui kutsuda samu meetode klasside kaudu. Staatilise meetodi süntaksit eespoolt saame kasutada ka laiendusmeetoditega.

// Ka selliselt saame laiendusmeetode kutsuda
Console.WriteLine(StringExtensions.Nl2Br(stringWithLineBreaks));
Console.WriteLine(StringExtensions.Repeat(stringToRepeat, 3));
Console.WriteLine(StringExtensions.ToMD5(valueToHash));
Console.WriteLine(StringExtensions.ToSha256(valueToHash));

Viited