5 Şubat 2017 Pazar

c# operator ön ekini kullanımına örnek

c# dilinin bize kazandırdığı güzel bir özellik operatör ön eki ile mevcut operatörler aşırı yüklenebileceği gibi aynı zamanda kendi nesnelerimiz için de operatör tanımlayabiliriz.

Burada yapacağımız örnek çalışma ile kendi nesnelerimiz için operatörler tanımlayıp onları ürettiğimiz program içerisinde kullanacağız.

Parasal işlemleri yaparken genelde decimal, bilimsel hesaplamalar yaparken ise double nesneleri kullanmaya özen gösteririz. Bu mali işlemlerde kesinliğe ve bilimsel hesaplarda sürate verdiğimiz önemden kaynaklanmaktadır. 

Bu örnekte biz bir veri tipi oluşturmak istiyoruz. Money isimli bu tipin iki property' si olacak. 

  • Value: decimal -> Paranın miktarı mesela 10.41
  • Code: Currency(enum) -> Döviz Kodu USD, TRL gibi
Bunun için ilk önce Döviz kodunu vereceğimiz Currency enum' ını oluşturalım.


1
2
3
4
5
6
7
namespace OperatorExample
{
    public enum Currency
    {
        USD, EUR, TRL
    }
}


Money veri tipi bir değer türünde olacağı için struct şeklinde oluşturmamız doğru olacaktır.



1
2
3
4
5
6
7
8
namespace OperatorExample
{
    public struct Money
    {
        public decimal Value;
        public Currency Code;
    }
}

Artık Money tipinde değişkenler tanımlayabiliriz.


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace OperatorExample
{
    class Program
    {
        static void Main(string[] args)
        {
            Money m1;
            m1.Value = 25.14M;
            m1.Code = Currency.EUR;

        }
    }
}

Dikkatinizden kaçmadı ise m1 i yaratırken bir constructor kullanmadık. Bunun sebebi structların int, decimal gibi bir değer tipi olmasıdır. 

c# bize structlar için constructor kullanmaya müsaade etmekte. Bunu daha kolay tanımlamak için kullanabiliriz. Burada dikkat etmeniz gereken husus new işlecini kullandığımızda bellekte yeni bir nesne oluşmaması sadece değişkenlere daha kolay tanımlama yapabilmemiz içindir.


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
namespace OperatorExample
{
    public struct Money
    {
        public decimal Value;
        public Currency Code;

        public Money(decimal value, Currency code)
        {
            Value = value;
            Code = code;
        }
    }
}


Artık bir değişkeni kolayca tanımlayabiliriz


1
Money m2 = new Money(45.1M, Currency.EUR);

Bu değer tipini tanımladıktan sonra bu iki değeri toplamak istersek nasıl yapacağız sorusu makalemizin de ilgilendiği konudur. 

Sıradan bir toplama işleminde iki integer sayıyı + operatörü ile toplayabiliyoruz. Peki şu işlemi yapmak istersek ne yapmamız gerekir?


1
var sum = m1 + m2;

bu noktara operator öneki devreye giriyor. Bizim Money nesnesi için toplama işlemini tanımlamamız gerekmekte. Money yapısına şu metodu ekleyelim


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
        public static Money operator + (Money m1, Money m2)
        {
            Money sum;


            sum.Value = m1.Value + m2.Value;
            sum.Code = m1.Code;

            return sum;
        }

Yalnız elmalar ile armutları toplamak istemeyiz. Bu istisnayı yakalamak için bir Exception üretelim.



using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace OperatorExample
{
    public class DifferentCurrencyException : Exception
    {
        public string[] CurrencyCodes;

        public DifferentCurrencyException(params Currency[] codes)
        {
            CurrencyCodes = codes.Select(x => x.ToString()).ToArray();
        }

        public override string Message
        {
            get
            {
                return "Farklı dövizler arasında işlem yapılamaz.";
            }
        }
    }
}

Gördüğünüz gibi exception'un Message propertysini kendi mesajımla ezdim (override).

Şimdi bunu + operatörünün içinde çağıralım


        public static Money operator + (Money m1, Money m2)
        {
            if(m1.Code != m2.Code)
            {
                throw new DifferentCurrencyException(m1.Code, m2.Code);
            }

            Money sum;
            sum.Value = m1.Value + m2.Value;
            sum.Code = m1.Code;

            return sum;
        }


Şimdi artık toplama işlemini yapabiliriz.













Gördüğünüz gibi toplama işlemini gerçekleştirebildik. Bir de bu değeri konsola yazdıralım. Console.WriteLine sınıfı bir çok değer tipine aşırı yüklenmiştir. Değişkenleri şöyle ayrı ayrı çağırarak da yapabiliriz.


Console.WriteLine("{0} {1}", sum.Value, sum.Code.ToString());

Peki her seferinde bunu mu yapacağız. bu değeri MessageBox.Show ile gösterebilir veya ekrandaki bir label'a yazdırabiliriz. Bu örnekte şöyle çağırabilmeliyiz.



Console.WriteLine(sum.ToString());

Böyle bir işlemi direk yaparsak



Doğrudan ToString Metodu çoğu bize nesnenin Namespace ile birlikte ismini verir.

Daha genel bir işlem yapmak için her nesnede bulunan ToString metodunu Money yapısı için ezmemiz (override) gerek.

public override string ToString()
{
      return string.Format("{0} {1}", this.Value.ToString("n2"), this.Code.ToString());
}

Artık doğrudan metni alabilir ve çağırabiliriz.


static void Main(string[] args)
{
    Money m1;
    m1.Value = 25.14M;
    m1.Code = Currency.EUR;

    Money m2 = new Money(45.1M, Currency.EUR);

    var sum = m1 + m2;

    Console.WriteLine(sum.ToString());

}


Buradan sonra genelde müşteriler bu işlemi beğenmeyecek ve sizden EUR yerine € simgesini basmanızı isteyecektir. Bu noktada bir kaç çözüm var tabii ki. CultureInfo sınıfından yararlanabilirsiniz. Currency enum'ını yine bir struct olarak tanımlayabilirsiniz.

Ben burada bu basit kütüphane için kendi yaklaşımımı deneyeceğim. Enum'daki değerlere char şeklinde simgelerini atayacağım.


namespace OperatorExample
{
    public enum Currency
    {
        USD = '$', EUR = '€', TRL = '₺'
    }
}

Bu değişiklikten sonra çalıştırdığımızda yine aynı sonucu aldık. O zaman kodumuzda ufak bir modifikasyona gitmemiz gerek




public override string ToString()
{
    return string.Format("{0} {1}", this.Value.ToString("n2"), ((char)this.Code).ToString());
}

Bir daha bakalım.



Doğru yoldayız. Tek bir karakter çıkarabildik ancak konsola utf-8 desteği vermek lazım. Bu da bir microsoft problemi olarak kenarda dursun. 

Main metodumuzun başına şu satırı ekliyoruz. 


Console.OutputEncoding = Encoding.UTF8;

Ve de mevcut konsol fontu utf-8 desteklemediği için programın başına debug koyup mevcut font'u lucida gibi bir fonta çekmemiz gerekmekte




Vee sonuç:)


Bir sonraki makalemde bu tipleri string olarak tanımlayarak operatör kullanımını implicit ve explicit operatörler üzerinden bir tık daha ileriye taşımak. 

Son olarak fırlattığımız istisna'yı (Exception) yakalamak adına toplama işlemini try bloğu içine alalım.


static void Main(string[] args)
{
    Console.OutputEncoding = Encoding.UTF8;
    Money m1;
    m1.Value = 25.14M;
    m1.Code = Currency.USD;

    Money m2 = new Money(45.1M, Currency.EUR);

    try
    {
        var sum = m1 + m2;
        Console.WriteLine(sum.ToString());
    }
    catch(DifferentCurrencyException dex)
    {
        string message = dex.Message;
        message += Environment.NewLine;
        message += "Döviz kodları : ";
        foreach(var code in dex.CurrencyCodes)
        {
            message += code + " ";
        }
        Console.WriteLine(message);
    }
    catch(Exception ex)
    {

    }




}


Kodları github' üzerinden indirebilirsiniz.

https://github.com/guraybaykan/ üzerinden beni takip edebilirsiniz. İyi günler dilerim :)