петак, 08. јануар 2016.

Konverzija tipova podataka u C# programskom jeziku


Bez obzira da li radite na malim programima ili velikim projektima često će te dolaziti u situaciju da konvertujete vrednost iz jednog tipa u drugi. Takođe, vi ne možete deklarisati istu promenjivu dvaput. Ako je promenjiva deklarisana kao integer vi ne možete njoj dodeliti vrednost tipa string. Odmah bi ste dobili grešku „Cannot implicitly convert type 'string' to 'int'.” U konzolnim aplikacijama vi unosite podatke putem tastature u obliku znakova i oni se prilikom ulaza tretiraju kao vrednost tipa string. Ukoliko podatak u vašem programu treba da bude broj tipa integer ili float neophodno je da konvertujete vrednost tipa string u integer ili string u float, kasnije možda i integer u float kako bi ste mogli da vršite aritmetičke operacije sa vašim brojevima; tada vršite konverziju vrednosti iz jednog tipa podataka u drugi.


( Slikovit prikaz konverzije tipova podataka )

Konverzija tipova podataka se uglavnom deli na implicitnu, eksplicitnu konverziju ili programerski definisanu konverziju. Implicitna konverzija je ona konverzija koja se vrši automatski, eksplicitna konverzija je ona konverzija koja se vrši kastingom i programerski definisana konverzija je konverzija koju je kodirao programer. Međutim, uopšteno konverzija tipova podataka se takođe vrši na više načina:

Korišćenjem kastinga
Korišćenjem as operatora
Korišćenjem Parsing-a
Korišćenjem klase System.Convert
Korišćenjem klase System.Bit
Boxing i Unboxing konverzija


Šta je widening i narrowing konverzija?



Widening - proširena konverzija je kad manji tip podataka konvertujete u veći. Npr.

short shortNumber = 100;
int intNumber = shortNumber;

Narrowing – sužena konverzija je kad veći tip podataka konvertujete u manji tip podataka. Npr.

int intNumber = 100;
short shortNumber = (short)intNumber;

Proširena konverzija je uvek uspešna i za nju nemate razloga da brinete. Međutim, sužena konverzija se ne može izvršiti eksplicitno; možete je izvršiti samo implicitno kastingom i pod uslovom da vrednost većeg tipa je u rangu vrednosti manjeg. Inače će te dobiti sasvim drugačiji rezultat od očekivanog. Da je u navedenom primeru vrednost intNumber jednaka 100 000 onda vam kompajler nebi prijavio grešku, ali vaš rezultat bi bio -31 702. Zato je mnogo bitno da budete oprezni sa suženom konverzijom. Ako želite da vam kompajler prijavi grešku kad dođe do prekoračenja vrednosti prilikom sužene konverzije u vreme izvršavanja koda koristite kontrolu prekoračenja Checked prilikom kastinga.

Kako se koriste operatori is i as?

Operatori is i as se koriste kod referetnih tipova podataka. Da bi ste razumeli navedene operatore neophodno je da improvizujemo minimum dve prazne klase, Person i Employee; stime da Employee klasa nasleđuje Person klasu, tako da nam je Person bazna klasa.

class Person {}
class Employee : Person {}  

Kad hoćete da ispitate da li je klasa određena klasa koristite operator is.

Person person = new Person();
if(person is Person) WriteLine("True");

Kad hoćete da se neka klasa ponaša kao njena bazna klasa, tada koristite operator as.

Employee employee = new Employee();
if ((employee as Person) is Person) WriteLine("True");

Možete se zapitati zašto bi ste koristili klasu Employee kao klasu Person, ali jednostavno možda će te imati potrebu u vašem programu da na listu osoba stavite i listu zaposlenih. Tada jednostavno možete dodeliti zaposlene kao osobe listi osoba koristeći operator as.

person = employee as Person;

Šta je Parsing?

Ukoliko redovno pratite i čitate moje postove primetiće te da smo već koristili konvertovanje string podataka prilikom preuzimanja vrednosti od korisnika sa konzole. Inače, Parsing – raščlanjivanje se u C# programskom jeziku koristi korišćenjem metoda Parse ili TryParse kada hoćete da tip string konvertujete u neki drugi tip. Pogledajte sliku na kojoj je predstavljeno koji tipovi podržavaju metode Parse i TryParse.

 
( Tipovi podataka koji podržavaju metode Parse i TryParse )

Metoda Parse direktno konvertuje vrednost tipa string u neki od navedenih tipova podataka i ukoliko se konverzija ne može izvesti iz bilo kog razloga; kompajler izbacuje grešku. Ukoliko niste sigurni da vaša konverzija će uvek biti uspešna, onda obavezno koristite metodu Parse u okviru konstrukcije try-catch-finally.

string stringNumber = "1000";
intNumber = Int32.Parse(stringNumber);    

Za razliku od metode Parse, metoda TryParse je tipa boolean i prvo proverava da li se može izvesti konvertovanje i ukoliko može, radnja će se izvršiti; metoda će vratiti True ili u suprotnom metoda će vratiti False. Ukoliko se ne može izvršiti konvertovanje stringa, kompajler neće izbaciti grešku.

string stringNumber = "1000";
bool correct = Int32.TryParse(stringNumber, out intNumber);

Konvertovanje tipova podataka korišćenjem klase Convert

Konvertovanje podataka korišćenjem klase Convert koja se nalazi u imenskom prostoru System je jednostavno ali i tada trebate obratiti pažnju na vrednost koju konvertujete posebno ako je vrednost pisana u nekom formatu koji nije samo broj. Npr. konverzija iz decimal vrednosti u integer jednostavno uklanja sve brojeve iza decimalne tačke.

stringNumber = "$999,999.99";
decimal decimalNumber = Decimal.Parse(stringNumber, NumberStyles.AllowCurrencySymbol
                                                   |NumberStyles.AllowThousands
                                                   |NumberStyles.AllowDecimalPoint);
intNumber = Decimal.ToInt32(decimalNumber);  

Rezultat navedenog koda je 999999. Možda bi ste vi hteli da zaokružite broj na 1000000. U tom slučaju bi ste morali da koristite metodu Round klase Math, jer klasa Convert ne zaokružuje brojeve prilikom konverzije.


( Metode za konverziju klase Convert u C# programskom jeziku ) 

Konvertovanje tipova podataka korišćenjem klase BitConverter

Klasa BitConverter je specijalizovana klasa koja vam omogućava da njenim metodama vrednosti konvertujete u niz bitova i obratno.

intNumber = 1000000;
byte[] byteArray = BitConverter.GetBytes(intNumber);
           
WriteLine(Environment.NewLine + $"System.BitConverter - Big Endian - int 1000000 to byte[]: {BitConverter.ToString(byteArray)}");

if (BitConverter.IsLittleEndian) Array.Reverse(byteArray);
WriteLine($"System.BitConverter - Little Endian - int 1000000 to byte[]: {BitConverter.ToString(byteArray)}");

if (BitConverter.IsLittleEndian) Array.Reverse(byteArray);
WriteLine($"System.BitConverter - byte[] {BitConverter.ToString(byteArray)} to int: {BitConverter.ToInt32(byteArray,0)}");

Rezultat navedene konverzije bi bio broj 1000000 konvertovan u niz od 4 bita 40-42-0F-00 i obratno ako koristite sistem Big-Eding. Ali ako koristite sistem Little-Eding rezultat je niz 00-0F-42-40. Koji sistem će te da koristite to najviše zavisi od jačine vašeg računara. Vi takođe trebate da obratite pažnju ukoliko ste konvertovali tip Int32, povratna vrednost u obliku tip-a UInt32 će vam vratiti sasvim drugačiji rezultat.

Šta je Boxing i Unboxing konverzija?

Boxing konverzija vam je kada neki tip podataka konvertujete u tip object.

intNumber = 1000;
object objectNumber = intNumber;

Unboxing vam je konverzija kada hoćete da konvertujete object tip u onaj koji vama treba. To se vrši kastingom.

intNumber = (int)objectNumber;

Praktičan primer svih navedeni konverzija

Pogledajte kod programa TypeConversions koji sadrži sve navedene konverzije:


using System;
using static System.Console;
using System.Globalization;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace TypeConversions
{
    class Person {}
    class Employee : Person {}

    class Program
    {
        static void Main(string[] args)
        {
            // Widening and narrowing conversions
            short shortNumber = 100;
            int intNumber = shortNumber;

            WriteLine("Type Conversions" + Environment.NewLine);
            WriteLine($"Widening Conversions - short 100 to int: {intNumber}");

            shortNumber = (short)intNumber;

            WriteLine($"Narrowing Conversions - int 100 to (short)int: {shortNumber}");

            // is and as operators
            Person person = new Person();

            if(person is Person) WriteLine(Environment.NewLine + "Operator is - person is Person type!");

            Employee employee = new Employee();
                               
            if ((employee as Person) is Person) WriteLine("Operator as - employee as Person type!");

            // Parse and TryParse conversions
            string stringNumber = "1000";
            intNumber = Int32.Parse(stringNumber);
            WriteLine(Environment.NewLine + $"Parse - string 1000 to int: {intNumber}");

            bool correct = Int32.TryParse(stringNumber, out intNumber);
            WriteLine($"TryParse - string 1000 to int: {correct}");

            // Conversions using Convert class
            stringNumber = "$999,999.99";
            decimal decimalNumber = Decimal.Parse(stringNumber, NumberStyles.AllowCurrencySymbol
                                                                | NumberStyles.AllowThousands
                                                                | NumberStyles.AllowDecimalPoint);

            intNumber = Decimal.ToInt32(decimalNumber);
            WriteLine(Environment.NewLine + $"System.Convert - string $999,999.99 to decimal to int: {intNumber}");

            // BitConvereter conversions
            intNumber = 1000000;
            byte[] byteArray = BitConverter.GetBytes(intNumber);
            WriteLine(Environment.NewLine + $"System.BitConverter - Big Endian - int 1000000 to byte[]: {BitConverter.ToString(byteArray)}");

            if (BitConverter.IsLittleEndian) Array.Reverse(byteArray);
            WriteLine($"System.BitConverter - Little Endian - int 1000000 to byte[]: {BitConverter.ToString(byteArray)}");

            if (BitConverter.IsLittleEndian) Array.Reverse(byteArray);
            WriteLine($"System.BitConverter - byte[] {BitConverter.ToString(byteArray)} to int: {BitConverter.ToInt32(byteArray,0)}");

            // Boxing und Unboxing conversions
            intNumber = 1000;
            object objectNumber = intNumber;
           
            WriteLine(Environment.NewLine + $"Boxing - int 1000 to object: {objectNumber}");

            intNumber = (int)objectNumber;
            WriteLine($"Unboxing - object 1000 to int: {intNumber}" + Environment.NewLine);

            ReadKey();

        }
    }
}

Kad pokrenete navedeni program rezultati će biti sledeći:

Type Conversions

Widening Conversions - short 100 to int: 100
Narrowing Conversions - int 100 to (short)int: 100

Operator is - person is Person type!
Operator as - employee as Person type!

Parse - string 1000 to int: 1000
TryParse - string 1000 to int: True

System.Convert - string $999,999.99 to decimal to int: 999999
System.BitConverter - Big Endian - int 1000000 to byte[]: 40-42-0F-00
System.BitConverter - Little Endian - int 1000000 to byte[]: 00-0F-42-40
System.BitConverter - byte[] 40-42-0F-00 to int: 1000000

Boxing - int 1000 to object: 1000
Unboxing - object 1000 to int: 1000


Kako izgleda prethodni program možete pogledati i na video-u:



( C# 6.0 Tutorial - Fundamentals - 31. Type Conversions )