C# – Extension Metot Kullanımı

Extension metotlar, mevcut sınıf ve struct yapılarının orjinalliklerini bozmadan yeni metotlar ve özellikler yazarak fonksiyonelliklerini genişletme ihtiyacından doğmuştur. Bu ihtiyacı var olan çözümlerle de (kalıtım, mevcut sınıfa metot ekleme, partial sınıf vs.) giderebiliriz. O halde neden extension metotları kullanıyoruz?

* .NET içinde yazılmış çok sayıda sealed olarak tanımlanmış tip ve sınıf mevcuttur. Bu durumu kendilerinden türeterek yazamayacağımız tip ve metotları extension metot kavramı ile aşabiliriz.

* Çalışılan projede çok sayıda sınıf var, yeni bir metot eklenmesi production ortamı için risk içeriyor olabilir. Bir metot için oluşturulacak partial sınıf mantığının bakımı da zorlaşabilir. Yine böyle bir durumda extension metotlar pratik bir yaklaşım olarak karşımıza çıkıyor.

Bir extension metot tanımlamak için;

* Statik bir sınıf içinde statik metot olarak tanımlanmalıdır.

* Extension metotta, genişletilecek sınıfın türü this anahtar sözcüğü ile parametre olarak alınmalıdır. Bu sınıf türü, this ile ele alınmazsa hiç bir zaman çağrılmayacaktır. Aynı zamanda bir metodu, kendi ismiyle genişletirseniz, yazılan extension metot yerine yine kendisi çağrılır.

internal static class StringExtensions
    {
        public static int CountWords(this string s)
        {
            var collection = Regex.Matches(s, @"[\S]+");
            return collection.Count;
        }
    }

Bu extension metot ile int sınıfını genişletmiş olduk, artık bu sınıftan türetilen objeler için kullanabiliriz.

Extension metotların IEnumerable objeler için kullanım örneklerini aşağıda bulabilirsiniz. Buna benzer faydalı extension metotları extensionmethod.net sitesinde bulabilir, siz de ekleyebilirsiniz. :)

public static class Extensions
{
    //listelerin null kontrolüne alternatif.
    public static IEnumerable<T> ToSafeList<T>(this List<T> source)
    {
        if (source != null)
            return source;
        return new T[0];
    }

    //ToList()'i belli bir sayı ile kullanmak için.
    public static List<T> ToList<T>(this IEnumerable<T> source, int count)
    {
        if (source == null)
            throw new ArgumentNullException("source");
        if (count < 0)
            throw new ArgumentOutOfRangeException("count");

        var list = new List<T>(count);
        list.AddRange(source);

        return list;
    }

    //ToArray()'i belli bir sayı ile kullanmak için.
    public static T[] ToArray<T>(this IEnumerable<T> source, int count)
    {
        if (source == null)
            throw new ArgumentNullException("source");
        if (count < 0)
            throw new ArgumentOutOfRangeException("count");

        var array = new T[count];
        var i = 0;

        foreach (var item in source)
        {
            array[i++] = item;
        }

        return array;
    }

}
Reklamlar

C# ve MongoDB ile MapReduce Kullanımı

MapReduce, kısaca Google’ın veri işleme, indexleme gibi işlemlerinin arka planında yer alan algoritmasıdır. Çok sayıda makina barındıran kümelerle oluşturulan dağıtık mimari ile verilerin toplanması ve analiz edilmesi sağlanır.

Dağıtık mimari dediğimiz yapıda tüm veriyi alan ana düğüm (master node), alt düğümlere (worker node) veri toplama işini dağıtır. Toplanan veriler birleştirilerek ana düğüme tekrar gönderilir. Reduce aşamasında ise veri analiz edilir. İlişkisel veritabanları mantığı ile düşünürsek; map, select ifadesini, reduce ise count, having, avg gibi ‘data-aggreagation- işlemlerine karşılık geliyor. NoSQL sistemlerinde bu işlemlerin biraz daha karmaşık olması sebebiyle MapReduce işimizi gerçekten kolaylaştırıyor. MongoDB’nin dökümantasyonundan aldığım aşağıdaki görsel durumu daha açıklayıcı hale getirebilir.

map-reduce

MapReduce yapısının Google’ın bir algoritması olduğunu söylemiştik. C# ile MapReduce yapısını kullanmak istiorsanız MongoDB üzerinde entegre olarak geliyor. MongoDB C# driver ı her versiyonda biraz farklılaşıyor. Aşağıdaki örnekte 1.9.2 sürümünü kullandığımı belirteyim. Siz de projede kullanmak için Package Manager Console üzerinden dahil edebilirsiniz.

PM> Install-Package mongocsharpdriver -Version 1.9.2

Örnek senaryomuzda Products adında basit bir koleksiyon üzerinde bazı işlemler yapacağız. Aşağıdaki sınıfla veritabanından çekeceğimiz veri için model oluşturuyoruz.

class Product
    {
        public string name { get; set; }
        public string category { get; set; }
        public double price { get; set; }
        public DateTime enterTime { get; set; }
    }

Oluşturduğumuz koleksiyon içine ürün eklemek için MongoCollection sınıfından bir liste oluşturup Insert ya da  InsertBatch metodunu kullanabiliriz. InsertBatch() birden fazla verinin eklenmesini sağlar. AddProducts() içinde localhost‘a bağlanmak için gereken işlemlere de yer verdim. Uri‘ye veritabanı isminizi (bu örnekte test) verdikten sonra koleksiyonlarınıza GetCollection() metodu ile ulaşabilirsiniz.

static void AddProducts()
        {
            const string uri = "mongodb://localhost/test";
            var client = new MongoClient(uri);
            var db = client.GetServer().GetDatabase(new MongoUrl(uri).DatabaseName);

            var collection = db.GetCollection<Product>("Products");

            var products = new List<Product>{
                new Product {   name = "potato", 
                                category = "vegetables",
                                price = 5.32, 
                                enterTime = new DateTime(2015, 09, 14) },

                new Product {   name = "eggplant", 
                                category = "vegetables", 
                                price = 3, 
                                enterTime = new DateTime(2015, 09, 14) },
            };

            collection.InsertBatch(products);
        }

Bu kısımdan sonra asıl işlemlere yani map – reduce – finalize fonksiyonlarına geçebiliriz. Bu fonksiyonların MongoDB için Javascript ile yazıldığını hatırlatalım. Verilerin analizini price değeri üzerinden yapmayı seçersek fonksiyonlarımız aşağıdaki gibi şekillenecektir.

string map = @"
               function() {
               var product = this;
               emit(product.category, { count: 1, price: product.price });
                            }";

map işleminde, emit fonksiyonu kendisine gelen parametreleri key - value şeklinde toplamaktadır. Burada key, veriyi neye göre gruplayacağımıza karar verdiğimiz değerken (bu örnekte product) diğer parametre olan value, ürünün fiyatı ve sayısı (1 olmak durumunda) olmaktadır. Farklı düğümler üzerinde topladığımız veriyi ana düğüme aktardıktan sonra reduce fonksiyonuna geçebiliriz.

string reduce = @"        
                            function(key, values) {
                                var result = {count: 0, price: 0 };

                                values.forEach(function(value){               
                                    result.count += value.count;
                                    result.price += value.price;
                                });

                                return result;
                            }";

Map işleminden sonra bir bütün halinde gönderilen veri analiz edilerek tekrar toplanarak result adında tek bir veri haline getiriliyor. Burada her ürünün fiyatları toplanıyor. Buraya kadar yaptığımız işlemleri daha görsel bir örnekle açıklamaya çalışalım.

apple 3.25
orange 5
melon 2
—————

cucumber 2
eggplant 3
potato 5,32
—————

PC 4000
TV 3400

Yukarıda kategorilerine göre (fruits, vegetables, electronics..) key – value şeklinde sıralanmış veriler görüyoruz. Bu veri yapısı aynı zamanda toplanmış verinin reduce işlemine hazır olduğu anlamına geliyor. Reduce işlemi sonrası ise şöyle bir yapı ile karşılaşıyoruz;

reduce

 

 

 

 

Görüldüğü gibi ürünler kategorilerine göre gruplanmış ve fiyatlarının toplamı da alınmış bir şekilde gözüküyor. count, price değerlerinin yanısıra average değeri de gözünüze çarpmış olabilir.

string finalize = @"
                                function(key, value){
      
                                  value.average = value.price / value.count;
                                  return value;

                                }";

 

finalize fonksiyonu isteğe bağlı kullanılan ve reduce işleminden geçen veri üzerinde başka işlemler yapılmak istendiğinde kullanılabilir. Burada her kategorinin fiyat ortalamasını almış oluyoruz. Map, reduce ve finalize fonksiyonlarını yazarak verilerin nasıl toplanacağını ve analiz edeceğini belirtmiş olduk. Ancak neye göre gruplanacağı sorusuna henüz cevap vermedik. Örneğin toplanan fiyat değerinin 100’den büyük olduğu verileri, sadece fruits kategorisindeki verileri ya da giriş tarihi (bu örnekte enterTime) geçen ay ve bugüne kadar olan ürünleri listelemek istediğimizde ne yapacağız?

var mapReduceQuery = Query.GT("price", 100);

var options = new MapReduceOptionsBuilder();
options.SetFinalize(finalize);
options.SetOutput(MapReduceOutput.Inline);
options.SetQuery(mapReduceQuery);
var results = collection.MapReduce(map, reduce, options);


foreach (var result in results.GetResults())
{
    Console.WriteLine(result.ToJson()+"\n");
}

 

mapReduceQuery nesnesi ile çok çeşitli kombinasyonlar yapıp sonuçları değerlendirebilirsiniz. Bu basit uygulama dışında MapReduce işlemlerinin kullanıldığı örneklere Facebook’un kullanıcı davranışı analizlerinde, Amazon’un sunduğu web servis hizmetlerinden Amazon Elastic MapReduceu ve Linkedin, Twitter, Yahoo gibi çok büyük verilerle uğraşan şirketlerin yoğun olarak kullandığını verebiliriz. Daha fazla bilgi için MapReduce algoritmasını kullanan açık kaynak kodlu Hadoop platformunu inceleyebilirsiniz.

LINQ Sorgularında Left/Right Join İşlemleri

Bu yazıda outer join işlemlerini Linq sorgularıyla nasıl gerçekleştirebileceğimize bakacağız. Inner join işleminde birden fazla tablonun sadece istenen kolonlarının eşleştiği bilgileri getirilmesi sağlanır. Ancak bazı durumlarda tabloların alınacak kesişim kümesini biz belirlemek isteyebiliriz. Bunu iki basit veri tipi ile açıklayalım. Kitap ve SatisTipi adlı iki sınıf olsun.

public class Kitap
    {
        public int KitapNo { get; set; }
        public string KitapAdi { get; set; }
    }
    public class SatisDetay
    {
        public int SatisNo { get; set; }
        public int KitapNo { get; set; }
        public string SatisTipi { get; set; }
    }

 

Örnek verilerimiz de aşağıdaki gibi olsun.

List<Kitap> ktpListe = new List<Kitap>
                {
                    new Kitap{KitapNo=1, KitapAdi="Kaiken"},
                    new Kitap{KitapNo=2, KitapAdi="Satori"},
                    new Kitap{KitapNo=3, KitapAdi="Şibumi"},
                    new Kitap{KitapNo=4, KitapAdi="Kişisel Bir Sorun"},
                    new Kitap{KitapNo=5, KitapAdi="Kappa"}
                };

            List<SatisDetay> stsListe = new List<SatisDetay>
                {
                    new SatisDetay{SatisNo=1, KitapNo=1, SatisTipi="Internet"},
                    new SatisDetay{SatisNo=2, KitapNo=2, SatisTipi="Magaza"},
                    new SatisDetay{SatisNo=3, KitapNo=3, SatisTipi="Internet"},
                    new SatisDetay{SatisNo=4, KitapNo=3, SatisTipi="Internet"}
                };

 

Eğer satışı gerçekleşen kitapların adını listelemek isteseydik inner join işlemini şu şekilde kullanabiliriz.

var kitaplar = (from k in ktpListe
                            join s in stsListe on k.KitapNo equals s.KitapNo
                            select k.KitapAdi
                                    ).Distinct();

 

Ancak satışı gerçekleşmeyen kitapları da aynı liste de görüntülemek isteseydik bu kez left join işlemi yapmamız gerekir. Satışı gerçekleşmeyen kitaplar için satış tipi NULL gelecektir. Burada DefaultIfEmpty() metodu dikkatinizi çekmiştir. a olarak ifade ettiğimiz tablodan b olarak ifade ettiğimiz SatisDetay tablosunda karşılığı boş olan kolonları da getirmesini sağladık.

var kitaplar = (from k in ktpListe
                            join s in stsListe on k.KitapNo equals s.KitapNo
                            into a from b in a.DefaultIfEmpty(new SatisDetay())
                            select k.KitapAdi
                                    ).Distinct();

 

Eğer mevcut tüm satış tiplerini getirmek istersek right join işlemi yapabiliriz.

var satisTipleri = (from s in stsListe
                            join k in ktpListe on s.KitapNo equals k.KitapNo
                            into a from b in a.DefaultIfEmpty(new Kitap())
                            select s.SatisTipi
                                   ).Distinct();

 

 

 

VQ5XP

C# : Metin İçinde Metin Arama – 2

İlk yazıda kullandığımız IndexOf metodu yerine aynı sorunu LINQ sorguları ile çözmeye çalışacağız. Öncelikle LINQ mimarisine bakarsak, üzerinde çalıştığımız çeşitli arayüzler içeren veri kümelerinde ve veri kaynaklarında(XML, JSON vs.) sorgulama yapmamızı sağlayan ifadelerdir. Aslında LINQ yapısı programlama diline, sorgulama becerisi ekler, sorgulama ve sonuç kümesi döndürme gibi işlemleri ise sağlayıcı(provider) dediğimiz yapılar gerçekleştirir. Bu sorun için LINQ yapısının veri kümeleri üzerinde sorgulama yapmamıza imkan veren ifadeler olduğunu bilmemiz yeterli. Yazıda bir metin içinde aranan kelimeyi LINQ to Objects sağlayıcısının bize sunduğu Count metodunu kullanarak bulmaya çalışacağız.

 string metin, aranan;
 char[] noktalama= { ',', '!', '#', '$', '%', '&', '(', ')', '*', '+', '-', /*'.',*/ '/', ':', ';', '<', '=', '>', '?', '@', '[', '/', ']', '^', '{', '|', '}', ' ' };
 Console.WriteLine("Metni giriniz: ");
 metin = Console.ReadLine().ToString();
 Console.WriteLine("Aranan kelimeyi giriniz: ");
 aranan = Console.ReadLine().ToString();
 string[] kelimeler = metin.Split(noktalama);
 int adet = kelimeler.Count(kelime => kelime == aranan);
 Console.WriteLine(aranan + " kelimesi " + adet + " adet bulunmustur.");
 Console.ReadLine();

C#: PadLeft ve PadRight Metotları

C# dilinin string sınıfı için sağladığı çok sayıda yararlı özellik bulunuyor, Pad metodu da karakter sayısının önemli olduğu işlemlerde kullanılan pratik bir metot.

PadLeft ve PadRight metotlarını kısaca açıklarsak string ifadelerimizin karakter sayısı ne olursa olsun belli bir karakter sayısına uymasını istiyorsak o sayıya tamamlamasını sağlıyor. Bir örnek olarak decimal sayıları binary olarak çevirdiğimizde soldaki 0 değerlerini göremeyiz, ancak uygulamada göstermek istiyorsak PadLeft metotundan yardım alabiliriz.

for (int i = 0; i < 16; i++)
  {
     dizi[i] = Convert.ToString(i, 2);
     dizi[i] = dizi[i].PadLeft(4, '0');
  }

Burada 0-15 arasındaki sayıları binary hale getiriyoruz ancak PadLeft metotunu kullanmasaydık örneğin 2 sayısı ’10’ olarak görülecekti. Biz string ifadenin değerine bakmaksızın 4 karakter halinde kaydetmiş olduk.

Metodun ilginç bir özelliği de aritmetik işlemlerde kullandığınızda yeni bir sayı olarak kabul etmesi. Ancak bu durum, metotların string bir ifadeyi değiştirmediğini, sadece istenen karakter sayısına tamamladığını unutmamak gerek.

string deger = "3";
int sonuc = Convert.ToInt32(deger.PadRight(2, '0')) - 1;
Console.Write(sonuc); // 29 yazdırır.
Console.Read();

Eğer string ifadenin soluna ya da sağına herhangi bir karakter yerine boşlukla doldurmak istiyorsak metoda sadece istenen karakter sayısını vermek yeterli oluyor.

string str = "'Solda 10 bosluk olacak'";
Console.Write("Pad Kullanımı");
Console.WriteLine(str.PadLeft(32));
Console.Read();

str değişkenimiz 22 karakterden oluştuğundan soldan 10 karakter boşluk bırakmak için PadLeft metoduna 32 değerini vermiş olduk. PadRight metodu da sağdan karakter doldurmak için PadLeft ile aynı mantıkta kullanılabilir.

C# : Kodun Çalışma Süresini Hesaplama

C#, içinde barındırdığı bir çok hazır metotla yazılan kodu tek satıra indirmeyi sağlıyor. Bu durum bir kodun çalışma süresini hesaplamak için de geçerli.

Bir kodun çalışma süresini hesaplamak çoğu zaman önemlidir. Fonksiyonların ve değişken sayısının süreye nasıl etki ettiğini satır satır gözlemlemek için ideal.

using System.Diagnostics;
...
StopWatch sw = new StopWatch();

sw.Start(); // Hangi satırdan önce koyarsak o satırdan sonrası için işlemeye başlar.
...
Console.WriteLine(sw.Elapsed.ToString());

C# : Metin İçinde Metin Arama – 1

Belirli bir metin içinde kelime ya da karakter arama, şifreleme algoritmalarında,uzun metinlerde kelime bulmak ya da belli bir konuma gelmek için sıklıkla kullanılır. Programla yapılmak istenen kullanıcıdan alınan kelimenin, verilen metinde ya da okunan dosyadaki metinde kaç adet olduğunu bulmaktır.

Kullanıcıdan alınan ve dosyadan okunan metin arasında kelime aramanın mantığı aynıdır. Burada yararlanılan IndexOf() metodu bize istenen kelimenin olup olmadığını döndürür. Eğer yoksa -1 değeri döndürecektir. sayac değişkeni bulunduğu takdirde 1 arttırılarak kaç kelimenin kaç adet olduğunu gösterecektir.

int konum = sifre.IndexOf(sifre);
while (konum != -1)
{
konum = kelime.IndexOf(sifre, konum + 1);
sayac++;
}

Eğer dosyadan okutmak isteniyorsa ek olarak dosyanın yerini belirtecek ve okuma yapacak StreamReader nesnesine ihtiyaç vardır. Aşağıdaki satırlarla str adlı bir nesne oluşturularak C dizinindeki metin adlı belgeye ulaşıldı. ReadLine() metodu ile oku adlı string değişkenine atandı.

string oku;
StreamReader str = new StreamReader("c:\\metin.txt");
Console.WriteLine("Aranmak istenen kelime?");
kelime = Console.ReadLine();
oku = str.ReadLine();

Programın tüm kodları ise şöyle;

int secim;
String sifre,kelime;
int sayac = 0;
Console.WriteLine("Metin girmek icin 1'e, dosyadan okutmak icin 2'ye basiniz:");
secim = Convert.ToInt32(Console.ReadLine());
switch (secim)
{
case 1:
Console.WriteLine("Sifreyi giriniz:");
sifre = Console.ReadLine();
Console.WriteLine("Aranmak istenen kelime?");
kelime = Console.ReadLine();
int konum1 = kelime.IndexOf(sifre);
while (konum1 != -1)
{
konum1 = kelime.IndexOf(sifre, konum1 + 1);
sayac++;
}
break;
case 2:
string oku;
StreamReader str = new StreamReader("c:\\metin.txt");
Console.WriteLine("Aranmak istenen kelime?");
kelime = Console.ReadLine();
oku = str.ReadLine();
int konum2 = oku.IndexOf(kelime);
while (konum2 != -1)
{
konum2 = oku.IndexOf(sifre, konum2 + 1);
sayac++;
}
break;
}
Console.WriteLine("Metin icerisinde"+sayac+"tane bulunmustur.");
Console.ReadLine();
}