Aracılığıyla paylaş


LINQ İşleçlerini Kullanarak Gözlemlenebilir Dizileri Sorgulama

Mevcut .NET Olayları ile Köprü Oluşturma bölümünde, mevcut .NET olaylarını abone olmak için gözlemlenebilir dizilere dönüştürdük. Bu konu başlığında, gözlemlenebilir dizilerin birinci sınıf niteliğini, genel LINQ işleçlerinin bu nesneleri işlemek için Rx derlemeleri tarafından sağlandığı IObservable<T> nesneleri olarak inceleyeceğiz. Çoğu işleç gözlemlenebilir bir dizi alır ve üzerinde mantık gerçekleştirir ve gözlemlenebilir başka bir dizi çıkışı oluşturur. Buna ek olarak, kod örneklerimizden görebileceğiniz gibi, sonuçta elde edilen diziyi tam gereksinimlerinize göre ayarlamak için birden çok işleci bir kaynak dizide zincirleyebilirsiniz.

Farklı İşleçler Kullanma

Daha önce basit diziler oluşturmak ve döndürmek için Oluşturma ve Oluşturma işleçlerini kullandık. Var olan .NET olaylarını gözlemlenebilir dizilere dönüştürmek için FromEventPattern işlecini de kullandık. Bu konu başlığında, verileri filtreleyebileceğiniz, gruplandırabilmeniz ve dönüştürebilmeniz için Gözlemlenebilir türündeki diğer statik LINQ işleçlerini kullanacağız. Bu tür işleçler gözlemlenebilir dizileri giriş olarak alır ve çıkış olarak gözlemlenebilir diziler üretir.

Farklı dizileri birleştirme

Bu bölümde, çeşitli gözlemlenebilir dizileri tek bir gözlemlenebilir dizide birleştiren bazı işleçleri inceleyeceğiz. Dizileri birleştirdiğimizde verilerin dönüştürülmediğine dikkat edin.

Aşağıdaki örnekte, iki diziyi tek bir dizide birleştirmek ve buna abone olmak için Concat işlecini kullanırız. Çizim amacıyla, x ile başlayan ve daha sonra y sıralı sayılar üreten bir tamsayı dizisi oluşturmak için çok basit Range(x, y) işlecini kullanacağız.

var source1 = Observable.Range(1, 3);
var source2 = Observable.Range(1, 3);
source1.Concat(source2)
       .Subscribe(Console.WriteLine);
Console.ReadLine();

Sonuç dizisinin olduğuna 1,2,3,1,2,3dikkat edin. Bunun nedeni Concat işlecini kullandığınızda, 1. sıra () tüm değerlerini göndermeyi bitirene kadar 2. sıranın (source1kaynak2) etkin olmamasıdır. Yalnızca tamamlandıktan sonra source1 , değerleri source2 sonuç dizisine göndermeye başlar. Abone daha sonra sonuç dizisinden tüm değerleri alır.

Bunu Merge işleciyle karşılaştırın. Aşağıdaki örnek kodu çalıştırırsanız alırsınız 1,1,2,2,3,3. Bunun nedeni, iki dizinin aynı anda etkin olması ve değerlerin kaynaklarda oluştukları sırada dışarı gönderilmesidir. Sonuç dizisi yalnızca son kaynak dizisi değerleri göndermeyi bitirdiğinde tamamlanır.

Birleştir'in çalışması için tüm kaynak gözlemlenebilir dizilerin aynı IObservable<T> türünde olması gerektiğini fark edin. Sonuç dizisi IObservable<T> türünde olacaktır. Dizinin ortasında bir OnError üretirse source1 , sonuçta elde edilen sıra hemen tamamlanır.

var source1 = Observable.Range(1, 3);
var source2 = Observable.Range(1, 3);
source1.Merge(source2)
       .Subscribe(Console.WriteLine);
Console.ReadLine();

Catch işleciyle başka bir karşılaştırma yapılabilir. Bu durumda, source1 herhangi bir hata source2 olmadan tamamlanırsa başlamaz. Bu nedenle, aşağıdaki örnek kodu çalıştırırsanız, yalnızca source2 (üreten 4,5,6) yoksayıldığından alırsınız1,2,3.

var source1 = Observable.Range(1, 3);
var source2 = Observable.Range(4, 3);
source1.Catch(source2)
       .Subscribe(Console.WriteLine);
Console.ReadLine();

Son olarak OnErrorResumeNext'e göz atalım. Bu işleç, bir hata nedeniyle tamamlanamasa source1 bile öğesine geçersource2. Aşağıdaki örnekte, bir özel durumla sonlandırılan bir diziyi temsil etse source1 de (Throw işlecini kullanarak), abone tarafından source2yayımlanan değerleri (1,2,3) alır. Bu nedenle, herhangi bir kaynak dizisinin herhangi bir hata üretmesini bekliyorsanız, abonenin bazı değerleri almaya devam edeceğini garanti etmek için OnErrorResumeNext kullanmak daha güvenli bir bahistir.

var source1 = Observable.Throw<int>(new Exception("An error has occurred."));
var source2 = Observable.Range(4, 3);
source1.OnErrorResumeNext(source2)
       .Subscribe(Console.WriteLine);
Console.ReadLine();

Tüm bu birleşim işleçlerinin çalışması için tüm gözlemlenebilir dizilerin aynı T türünde olması gerektiğini fark edin.

Projeksiyon

Select işleci, gözlemlenebilir bir dizinin her öğesini başka bir forma çevirebilir.

Aşağıdaki örnekte, sırasıyla n uzunluğundaki dizelere bir tamsayı dizisi yansıtıyoruz.

var seqNum = Observable.Range(1, 5);
var seqString = from n in seqNum
                select new string('*', (int)n);
seqString.Subscribe(str => { Console.WriteLine(str); });
Console.ReadKey();

Mevcut .NET Olaylarıyla Köprü Oluşturma konusunda gördüğümüz .NET olay dönüştürme örneğinin bir uzantısı olan aşağıdaki örnekte, IEventPattern<MouseEventArgs> veri türünü Bir Nokta türüne yansıtmak için Select işlecini kullanıyoruz. Bu şekilde, bir fare taşıma olay dizisini bir sonraki "Filtreleme" bölümünde görülebileceği gibi ayrıştırılıp daha fazla değiştirilebilen bir veri türüne dönüştürüyoruz.

var frm = new Form();
IObservable<EventPattern<MouseEventArgs>> move = Observable.FromEventPattern<MouseEventArgs>(frm, "MouseMove");
IObservable<System.Drawing.Point> points = from evt in move
                                          select evt.EventArgs.Location;
points.Subscribe(pos => Console.WriteLine("mouse at " + pos));
Application.Run(frm);

Son olarak SelectMany işlecine bakalım. SelectMany işlecinin birçok aşırı yüklemesi vardır ve bunlardan biri seçici işlev bağımsız değişkenini alır. Bu seçici işlevi, kaynak gözlemlenebilir tarafından gönderilen her değerde çağrılır. Bu değerlerin her biri için seçici bunu mini gözlemlenebilir bir diziye dönüştürür. Sonunda, SelectMany işleci bu mini dizilerin tümünü tek bir sonuç dizisine düzleştirir ve ardından aboneye gönderilir.

SelectMany'den döndürülen gözlemlenebilir, kaynak dizi ve seçici tarafından üretilen tüm mini gözlemlenebilir diziler tamamlandıktan sonra OnCompleted'i yayımlar. Kaynak akışta bir hata oluştuğunda, seçici işlevi tarafından bir özel durum oluştuğunda veya mini gözlemlenebilir dizilerden herhangi birinde bir hata oluştuğunda OnError'ı tetikler.

Aşağıdaki örnekte, önce her 5 saniyede bir tamsayı üreten bir kaynak dizisi oluşturup yalnızca üretilen ilk 2 değeri almaya karar verdik (Take işlecini kullanarak). Ardından bu tamsayıların her birini başka bir dizisi kullanarak yansıtmak {100, 101, 102}için kullanırızSelectMany. Bunu yaptığınızda, iki mini gözlemlenebilir dizi oluşturulur {100, 101, 102} ve {100, 101, 102}. Bunlar son olarak tek bir tamsayı {100, 101, 102, 100, 101, 102} akışına düzleştirilir ve gözlemciye itilir.

var source1 = Observable.Interval(TimeSpan.FromSeconds(5)).Take(2);
var proj = Observable.Range(100, 3);
var resultSeq = source1.SelectMany(proj);

var sub = resultSeq.Subscribe(x => Console.WriteLine("OnNext : {0}", x.ToString()),
                              ex => Console.WriteLine("Error : {0}", ex.ToString()),
                              () => Console.WriteLine("Completed"));
Console.ReadKey();

Filtreleme

Aşağıdaki örnekte, basit bir gözlemlenebilir sayı dizisi oluşturmak için Generate işlecini kullanırız. Generate işlecinin çeşitli aşırı yüklemeleri vardır. Örneğimizde ilk durumu (örneğimizde 0), sonlandıracak koşullu işlevi (10'dan az), yineleyiciyi (+1), sonuç seçiciyi (geçerli değerin kare işlevi) alır. yazın ve Where ve Select işleçlerini kullanarak yalnızca 15'ten küçük olanları yazdırın.

  
IObservable<int> seq = Observable.Generate(0, i => i < 10, i => i + 1, i => i * i);
IObservable<int> source = from n in seq
                          where n < 5
                          select n;
source.Subscribe(x => {Console.WriteLine(x);});   // output is 0, 1, 4, 9
Console.ReadKey();

Aşağıdaki örnek, bu konunun önceki bölümlerinde gördüğünüz yansıtma örneğinin bir uzantısıdır. Bu örnekte, IEventPattern<MouseEventArgs> veri türünü bir Nokta türüne yansıtmak için Select işlecini kullandık. Aşağıdaki örnekte, yalnızca ilgilendiğimiz fare hareketlerini seçmek için Where ve Select işlecini kullanırız. Bu durumda, farenin ilk bisektör üzerindekilere (x ve y koordinatlarının eşit olduğu) hareketlerine filtre uygulayacağız.

var frm = new Form(); 
IObservable<EventPattern<MouseEventArgs>> move = Observable.FromEventPattern<MouseEventArgs>(frm, "MouseMove");
IObservable<System.Drawing.Point> points = from evt in move
                                          select evt.EventArgs.Location;
var overfirstbisector = from pos in points
                        where pos.X == pos.Y 
                        select pos;
var movesub = overfirstbisector.Subscribe(pos => Console.WriteLine("mouse at " + pos));
Application.Run(frm);

Zamana Dayalı İşlem

Zaman tabanlı işlemler gerçekleştirmek için Buffer işleçlerini kullanabilirsiniz.

Gözlemlenebilir bir dizi arabelleğe alındığında, gözlemlenebilir bir dizinin değerleri belirtilen zaman aralığına veya bir sayı eşiğine göre arabelleğe konur. Bu durum özellikle sıra tarafından çok büyük miktarda veri gönderilmesini beklediğiniz ve abonenin bu değerleri işleyebileceğiniz kaynağa sahip olmadığı durumlarda yararlıdır. Sonuçları zamana veya sayıya göre arabelleğe alarak ve yalnızca ölçütler aşıldığında (veya kaynak dizisi tamamlandığında) bir değer dizisi döndürerek abone OnNext çağrılarını kendi hızında işleyebilir. 

Aşağıdaki örnekte ilk olarak her saniye için basit bir tamsayı dizisi oluşturacağız. Ardından Buffer işlecini kullanır ve her arabelleğin diziden 5 öğe barındıracağını belirtiriz. Arabellek dolduğunda OnNext çağrılır. Ardından Sum işlecini kullanarak arabelleğin toplamını hesaplayacağız. Arabellek otomatik olarak boşaltılır ve başka bir döngü başlar. Çıktıda 10=0+1+2+3+4, 35=5+6+7+8+9 vb. olacaktır 10, 35, 60… .

var seq = Observable.Interval(TimeSpan.FromSeconds(1));
var bufSeq = seq.Buffer(5);
bufSeq.Subscribe(values => Console.WriteLine(values.Sum()));
Console.ReadKey();

Belirtilen zaman aralığına sahip bir arabellek de oluşturabiliriz. Aşağıdaki örnekte arabellek, 3 saniye boyunca birikmiş öğeleri barındıracaktır. Çıktı 3, 12, 21... 3=0+1+2, 12=3+4+5 vb.

var seq = Observable.Interval(TimeSpan.FromSeconds(1));
var bufSeq = seq.Buffer(TimeSpan.FromSeconds(3));
bufSeq.Subscribe(value => Console.WriteLine(value.Sum()));  
Console.ReadKey();

Buffer veya Window kullanıyorsanız, filtrelemeden önce dizinin boş olmadığından emin olmanız gerekir.

Kategorilere Göre LINQ İşleçleri

Kategorilere Göre LINQ İşleçleri konu başlığı altında, Kategorilere göre Gözlemlenebilir türü tarafından uygulanan tüm büyük LINQ işleçleri listelenir; özellikle: oluşturma, dönüştürme, birleştirme, işlevsel, matematiksel, zaman, özel durumlar, çeşitli, seçim ve ilkeller.

Ayrıca Bkz.

Başvuru

Observable

Kavramlar

Kategorilere Göre LINQ İşleçleri