Not
Bu sayfaya erişim yetkilendirme gerektiriyor. Oturum açmayı veya dizinleri değiştirmeyi deneyebilirsiniz.
Bu sayfaya erişim yetkilendirme gerektiriyor. Dizinleri değiştirmeyi deneyebilirsiniz.
.NET olayları genellikle bilinen birkaç deseni izler. Bu desenleri standartlaştırmak, geliştiricilerin herhangi bir .NET olay programına uygulanabilen bu standart desenler hakkında bilgi uygulayabileceği anlamına gelir.
Standart olay kaynakları oluşturmak ve kodunuzda standart olaylara abone olmak ve işlemek için ihtiyacınız olan tüm bilgilere sahip olmak için bu standart desenleri inceleyelim.
Olay temsilcisi imzaları
.NET olay temsilcisinin standart imzası:
void EventRaised(object sender, EventArgs args);
Bu standart imza, olayların ne zaman kullanıldığına ilişkin içgörü sağlar:
- Dönüş türügeçersiz. Olaylar sıfır veya daha fazla dinleyiciye sahip olabilir. Bir olayı tetiklemek tüm dinleyicilere bildirir. Genel olarak dinleyiciler olaylara yanıt olarak değer sağlamaz.
-
Olaylar göndereningösterir: Olay imzası, olayı oluşturan nesneyi içerir. Bu, herhangi bir dinleyiciye gönderenle iletişim kurmak için bir mekanizma sağlar. Derleme zamanı türü
sender
, daha türetilmiş ve her zaman doğru olan bir türü biliyor olmanıza rağmenSystem.Object
'dir. Kurala göre kullanınobject
. -
Olaylartek bir yapıda ek bilgiler paketler:
args
parametresi, ek gerekli bilgileri içeren System.EventArgs türetilmiş bir türdür. (Sonraki bölümünde bu kuralın artık uygulanmadığını göreceksiniz.) Olay türünüzün daha fazla bağımsız değişkene ihtiyacı yoksa, yine de her iki bağımsız değişkeni de sağlamanız gerekir. Olayınızın ek bilgi içermediğini belirtmek için kullanmanız gereken özel bir değer EventArgs.Empty.
Şimdi bir dizindeki dosyaları veya bir deseni izleyen alt dizinlerini listeleyen bir sınıf oluşturalım. Bu bileşen, desenle eşleşen her dosya için bir olay oluşturur.
Olay modeli kullanmak bazı tasarım avantajları sağlar. Aranan bir dosya bulunduğunda farklı eylemler gerçekleştiren birden çok olay dinleyicisi oluşturabilirsiniz. Farklı dinleyicileri birleştirmek daha güçlü algoritmalar oluşturabilir.
Aranan dosyayı bulmak için ilk olay bağımsız değişkeni bildirimi aşağıdadır:
public class FileFoundArgs : EventArgs
{
public string FoundFile { get; }
public FileFoundArgs(string fileName) => FoundFile = fileName;
}
Bu tür küçük, yalnızca veri türü gibi görünse de, kuralı izlemeli ve bunu bir başvuru (class
) türü yapmalısınız. Bu, bağımsız değişken nesnesinin referansla iletildiğini ve verilerdeki tüm güncellemelerin tüm aboneler tarafından gözlemlenebilir olduğu anlamına gelir. İlk sürüm sabit bir nesnedir. Etkinlik bağımsız değişken türünüzdeki özelliklerin değişmez olmasını tercih etmelisiniz. Bu şekilde, bir abone değerleri başka bir abone görmeden değiştiremez. (Daha sonra gördüğünüz gibi bu uygulamada özel durumlar vardır.)
Ardından FileSearcher sınıfında olay bildirimini oluşturmamız gerekir. System.EventHandler<TEventArgs> türünü kullanmak, henüz başka bir tür tanımı oluşturmanız gerekmeyecek anlamına gelir. Yalnızca genel bir özelleştirme kullanırsınız.
Bir desenle eşleşen dosyaları aramak için FileSearcher sınıfını dolduralım ve bir eşleşme bulunduğunda doğru olayı tetikleyelim.
public class FileSearcher
{
public event EventHandler<FileFoundArgs>? FileFound;
public void Search(string directory, string searchPattern)
{
foreach (var file in Directory.EnumerateFiles(directory, searchPattern))
{
FileFound?.Invoke(this, new FileFoundArgs(file));
}
}
}
Alan benzeri olayları tanımlama ve oluşturma
Sınıfınıza olay eklemenin en basit yolu, önceki örnekte olduğu gibi bu olayı genel bir alan olarak bildirmektir:
public event EventHandler<FileFoundArgs>? FileFound;
Bu, kötü bir nesne odaklı uygulama gibi görünen bir genel alan bildiriyor gibi görünüyor. Özellikler veya yöntemler aracılığıyla veri erişimini korumak istiyorsunuz. Bu kod kötü bir uygulama gibi görünse de, derleyici tarafından oluşturulan kod sarmalayıcılar oluşturur, böylece olay nesnelerine yalnızca güvenli yollarla erişilebilir. Alan benzeri bir olayda kullanılabilen tek işlem işleyici ekleme'dir:
var fileLister = new FileSearcher();
int filesFound = 0;
EventHandler<FileFoundArgs> onFileFound = (sender, eventArgs) =>
{
Console.WriteLine(eventArgs.FoundFile);
filesFound++;
};
fileLister.FileFound += onFileFound;
ve işleyicisini kaldırın:
fileLister.FileFound -= onFileFound;
İşleyici için yerel bir değişken vardır. Lambda gövdesini kullandıysanız kaldırma düzgün çalışmaz. Temsilcinin farklı bir örneği olur ve sessizce hiçbir şey yapmaz.
Sınıfın dışındaki kod olayı tetikleyemez ve başka işlemler gerçekleştiremez.
Olay abonelerinden değer döndürme
Basit sürümünüz sorunsuz çalışıyor. Şimdi başka bir özellik ekleyelim: İptal.
Bulundu olayını yükselttiğinizde, bu dosya aranan son dosyaysa dinleyicilerin daha fazla işlemeyi durdurabilmeleri gerekir.
Olay işleyicileri bir değer döndürmez, bu nedenle bunu başka bir yolla iletmeniz gerekir. Standart olay düzeni, olay abonelerinin iptali iletmek için kullanabileceği alanları eklemek amacıyla EventArgs
nesnesini kullanır.
İptal sözleşmesinin semantiğine göre iki farklı desen kullanılabilir. Her iki durumda da, bulunan dosya olayı için EventArguments öğesine bir boole alanı eklersiniz.
Tek bir düzen, herhangi bir abonenin işlemi iptal etmesine olanak tanır. Bu desen için yeni alan false
olarak başlatılır. Herhangi bir abone bunu olarak true
değiştirebilir. Olay tüm abonelere başlatıldıktan sonra FileSearcher bileşeni Boolean değerini inceler ve eyleme geçer.
İkinci desen yalnızca tüm aboneler işlemin iptalini isterse işlemi iptal eder. Bu düzende, işlemin iptal edilmesi gerektiğini belirtmek için yeni alan başlatılır ve herhangi bir abone işlemin devam etmesi gerektiğini belirtmek için bu alanı değiştirebilir. Tüm aboneler yükseltilmiş olayı işledikten sonra, FileSearcher bileşeni boolean değerini inceler ve gerekli adımları atar. Bu düzende ek bir adım vardır: bileşenin olaya yanıt veren abone olup olmadığını bilmesi gerekir. Abone yoksa, alan hatalı bir iptal olduğunu gösterir.
Şimdi bu örnek için ilk sürümü uygulayalım. Türüne CancelRequested
adlı bir boole alanı eklemeniz gerekir:
public class FileFoundArgs : EventArgs
{
public string FoundFile { get; }
public bool CancelRequested { get; set; }
public FileFoundArgs(string fileName) => FoundFile = fileName;
}
Bu yeni alan otomatik olarak false
olarak başlatılır, bu da yanlışlıkla iptal etmemeniz içindir. Bileşende yapılan tek başka değişiklik, olayı tetikledikten sonra, abonelerden herhangi biri iptal talebinde bulundu mu diye bayrağı kontrol etmektir.
private void SearchDirectory(string directory, string searchPattern)
{
foreach (var file in Directory.EnumerateFiles(directory, searchPattern))
{
var args = new FileFoundArgs(file);
FileFound?.Invoke(this, args);
if (args.CancelRequested)
break;
}
}
Bu düzenin bir avantajı, hataya neden olan bir değişiklik olmadığıdır. Abonelerden hiçbiri daha önce iptal isteğinde bulunmuyor ve hala iptal etmiyor. Abone kodundan hiçbiri, yeni iptal protokolünün desteklenmediği sürece güncelleştirme gerektirmez.
İlk yürütülebilir dosyayı bulduğunda iptal isteğinde bulunabilmesi için aboneyi güncelleştirelim:
EventHandler<FileFoundArgs> onFileFound = (sender, eventArgs) =>
{
Console.WriteLine(eventArgs.FoundFile);
eventArgs.CancelRequested = true;
};
Başka bir olay bildirimi ekleme
Şimdi bir özellik daha ekleyelim ve olaylar için diğer dil deyimlerini gösterelim. Hadi dosyaları aramak için tüm alt dizinlerden geçen Search
yönteminin fazla yüklemesini ekleyelim.
Bu yöntem, birçok alt dizini olan bir dizinde uzun bir işlem olabilir. Şimdi her yeni dizin araması başladığında tetiklenen bir olay ekleyelim. Bu olay, abonelerin ilerleme durumunu izlemesine ve kullanıcıyı ilerleme hakkında bilgilendirmesine olanak tanır. Şimdiye kadar oluşturduğunuz tüm örnekler geneldir. Şimdi bu olayı bir iç olay haline getirelim. Bu, argüman türlerini de içsel yapabileceğiniz anlamına gelir.
Yeni dizini ve ilerleme durumunu raporlamak için türetilmiş yeni EventArgs sınıfını oluşturarak başlarsınız.
internal class SearchDirectoryArgs : EventArgs
{
internal string CurrentSearchDirectory { get; }
internal int TotalDirs { get; }
internal int CompletedDirs { get; }
internal SearchDirectoryArgs(string dir, int totalDirs, int completedDirs)
{
CurrentSearchDirectory = dir;
TotalDirs = totalDirs;
CompletedDirs = completedDirs;
}
}
Yine önerileri izleyerek olay bağımsız değişkenleri için sabit bir başvuru türü oluşturabilirsiniz.
Ardından olayı tanımlayın. Bu kez farklı bir söz dizimi kullanırsınız. Alan söz dizimini kullanmaya ek olarak, ekleme ve kaldırma işleyicileriyle olay özelliğini açıkça oluşturabilirsiniz. Bu örnekte, bu işleyicilerde ek koda ihtiyacınız yoktur, ancak bu, bunları nasıl oluşturabileceğinizi gösterir.
internal event EventHandler<SearchDirectoryArgs> DirectoryChanged
{
add { _directoryChanged += value; }
remove { _directoryChanged -= value; }
}
private EventHandler<SearchDirectoryArgs>? _directoryChanged;
Burada yazdığınız kod, daha önce gördüğünüz alan olayı tanımları için derleyicinin oluşturduğu kodu birçok şekilde yansıtır. olayı, özelliklerine benzer söz dizimini kullanarak oluşturursunuz. İşleyicilerin farklı adlara sahip olduğuna dikkat edin: add
ve remove
. Bu erişimciler etkinliğe abone olmak veya abonelikten çıkmak için çağrılır. Olay değişkenini depolamak için özel bir yedekleme alanı da bildirmeniz gerektiğine dikkat edin. Bu değişken null olarak başlatılır.
Şimdi alt dizinleri tarayan ve her iki olayı da başlatan Search
yönteminin bir aşırı yüklemesini ekleyelim. En kolay yol, tüm dizinlerde arama yapmak istediğinizi belirtmek için varsayılan bir bağımsız değişken kullanmaktır:
public void Search(string directory, string searchPattern, bool searchSubDirs = false)
{
if (searchSubDirs)
{
var allDirectories = Directory.GetDirectories(directory, "*.*", SearchOption.AllDirectories);
var completedDirs = 0;
var totalDirs = allDirectories.Length + 1;
foreach (var dir in allDirectories)
{
_directoryChanged?.Invoke(this, new (dir, totalDirs, completedDirs++));
// Search 'dir' and its subdirectories for files that match the search pattern:
SearchDirectory(dir, searchPattern);
}
// Include the Current Directory:
_directoryChanged?.Invoke(this, new (directory, totalDirs, completedDirs++));
SearchDirectory(directory, searchPattern);
}
else
{
SearchDirectory(directory, searchPattern);
}
}
private void SearchDirectory(string directory, string searchPattern)
{
foreach (var file in Directory.EnumerateFiles(directory, searchPattern))
{
var args = new FileFoundArgs(file);
FileFound?.Invoke(this, args);
if (args.CancelRequested)
break;
}
}
Bu noktada, tüm alt dizinleri aramak için aşırı yüklemeyi çağıran uygulamayı çalıştırabilirsiniz. Yeni DirectoryChanged
olayı üzerinde abone yok, ancak ?.Invoke()
deyimini kullanmak doğru çalışmasını garanti eder.
Konsol penceresinde ilerleme durumunu gösteren bir satır yazmak için bir işleyici ekleyelim.
fileLister.DirectoryChanged += (sender, eventArgs) =>
{
Console.Write($"Entering '{eventArgs.CurrentSearchDirectory}'.");
Console.WriteLine($" {eventArgs.CompletedDirs} of {eventArgs.TotalDirs} completed...");
};
.NET ekosistemi genelinde takip edilen desenler gördünüz. Bu desenleri ve kuralları öğrenerek, C# ve .NET'i doğal ve alışılmış bir şekilde hızlıca yazıyorsunuz.
Ayrıca bkz.
Ardından, .NET'in en son sürümünde bu desenlerde bazı değişiklikler görürsünüz.