Dizin Oluşturucular

Dizin oluşturucular özelliklere benzer. Dizin oluşturucular birçok şekilde özelliklerle aynı dil özelliklerini temel alır. Dizin oluşturucular dizine alınmış özellikleri etkinleştirir: bir veya daha fazla bağımsız değişken kullanılarak başvuruldu özellikler. Bu bağımsız değişkenler bazı değerler koleksiyonuna bir dizin sağlar.

Dizin Oluşturucu Söz Dizimi

Bir dizin oluşturucuya değişken adı ve köşeli ayraçlar aracılığıyla erişilir. Dizin oluşturucu bağımsız değişkenlerini köşeli ayraçların içine yerleştirirsiniz:

var item = someObject["key"];
someObject["AnotherKey"] = item;

Özellik adı olarak anahtar sözcüğünü this kullanarak ve bağımsız değişkenleri köşeli ayraç içinde bildirerek dizin oluşturucuları bildirirsiniz. Bu bildirim, önceki paragrafta gösterilen kullanımla eşleşer:

public int this[string key]
{
    get { return storage.Find(key); }
    set { storage.SetAt(key, value); }
}

Bu ilk örnekte, özellikler ve dizin oluşturucular için söz dizimi arasındaki ilişkiyi görebilirsiniz. Bu benzetme, dizin oluşturucular için söz dizimi kurallarının çoğunu taşır. Dizin oluşturucuların geçerli erişim değiştiricileri (genel, korumalı iç, korumalı, iç, özel veya özel korumalı) olabilir. Korumalı, sanal veya soyut olabilir. Özelliklerde olduğu gibi, dizin oluşturucuda get ve set erişimcileri için farklı erişim değiştiricileri belirtebilirsiniz. Ayrıca salt okunur dizin oluşturucular (küme erişimcisini atlayarak) veya salt yazma dizin oluşturucuları (get erişimcisini atlayarak) belirtebilirsiniz.

Özelliklerle çalışmaktan öğrendiğiniz neredeyse her şeyi dizin oluşturuculara uygulayabilirsiniz. Bu kuralın tek istisnası otomatik uygulanan özelliklerdir. Derleyici her zaman bir dizin oluşturucu için doğru depolamayı oluşturamaz.

Bir öğe kümesindeki bir öğeye başvurmak için bağımsız değişkenlerin varlığı, dizin oluşturucuları özelliklerden ayırır. Her dizin oluşturucu için bağımsız değişken listeleri benzersiz olduğu sürece, bir tür üzerinde birden çok dizin oluşturucu tanımlayabilirsiniz. Şimdi sınıf tanımında bir veya daha fazla dizin oluşturucu kullanabileceğiniz farklı senaryoları inceleyelim.

Senaryolar

API'leri, bu koleksiyon için bağımsız değişkenleri tanımladığınız bir koleksiyonu modellediğinde, türünüzde dizin oluşturucular tanımlayabilirsiniz. Dizin oluşturucularınız doğrudan .NET core çerçevesinin parçası olan koleksiyon türleriyle eşlenebilir veya eşleşmeyebilir. Türünüz bir koleksiyonu modellemeye ek olarak başka sorumluluklara da sahip olabilir. Dizin oluşturucular, bu soyutlama değerlerinin nasıl depolandığına veya hesaplandığına ilişkin iç ayrıntıları ortaya çıkarmadan türünüzün soyutlaması ile eşleşen API'yi sağlamanıza olanak tanır.

Dizin oluşturucuları kullanmaya yönelik bazı yaygın senaryoları inceleyelim. Dizin oluşturucular için örnek klasöre erişebilirsiniz. İndirme yönergeleri için bkz . Örnekler ve Öğreticiler.

Diziler ve Vektörler

Dizin oluşturucu oluşturmaya yönelik en yaygın senaryolardan biri, türünüzün bir dizi veya vektör modellediğinde ortaya çıktığı senaryodur. Sıralı bir veri listesini modellemek için bir dizin oluşturucu oluşturabilirsiniz.

Kendi dizin oluşturucunuzu oluşturmanın avantajı, bu koleksiyon için depolama alanını gereksinimlerinize uyacak şekilde tanımlayabilmenizdir. Türünüzün aynı anda belleğe yüklenemeyecek kadar büyük geçmiş verileri modellediği bir senaryo düşünün. Kullanımı temel alarak koleksiyonun bölümlerini yüklemeniz ve kaldırmanız gerekir. Aşağıdaki örnek bu davranışı modeller. Kaç veri noktası olduğunu bildirir. İsteğe bağlı olarak verilerin bölümlerini tutmak için sayfalar oluşturur. Daha yeni isteklerin ihtiyaç duyduğu sayfalara yer açmak için sayfaları bellekten kaldırır.

public class DataSamples
{
    private class Page
    {
        private readonly List<Measurements> pageData = new List<Measurements>();
        private readonly int startingIndex;
        private readonly int length;
        private bool dirty;
        private DateTime lastAccess;

        public Page(int startingIndex, int length)
        {
            this.startingIndex = startingIndex;
            this.length = length;
            lastAccess = DateTime.Now;

            // This stays as random stuff:
            var generator = new Random();
            for(int i=0; i < length; i++)
            {
                var m = new Measurements
                {
                    HiTemp = generator.Next(50, 95),
                    LoTemp = generator.Next(12, 49),
                    AirPressure = 28.0 + generator.NextDouble() * 4
                };
                pageData.Add(m);
            }
        }
        public bool HasItem(int index) =>
            ((index >= startingIndex) &&
            (index < startingIndex + length));

        public Measurements this[int index]
        {
            get
            {
                lastAccess = DateTime.Now;
                return pageData[index - startingIndex];
            }
            set
            {
                pageData[index - startingIndex] = value;
                dirty = true;
                lastAccess = DateTime.Now;
            }
        }

        public bool Dirty => dirty;
        public DateTime LastAccess => lastAccess;
    }

    private readonly int totalSize;
    private readonly List<Page> pagesInMemory = new List<Page>();

    public DataSamples(int totalSize)
    {
        this.totalSize = totalSize;
    }

    public Measurements this[int index]
    {
        get
        {
            if (index < 0)
                throw new IndexOutOfRangeException("Cannot index less than 0");
            if (index >= totalSize)
                throw new IndexOutOfRangeException("Cannot index past the end of storage");

            var page = updateCachedPagesForAccess(index);
            return page[index];
        }
        set
        {
            if (index < 0)
                throw new IndexOutOfRangeException("Cannot index less than 0");
            if (index >= totalSize)
                throw new IndexOutOfRangeException("Cannot index past the end of storage");
            var page = updateCachedPagesForAccess(index);

            page[index] = value;
        }
    }

    private Page updateCachedPagesForAccess(int index)
    {
        foreach (var p in pagesInMemory)
        {
            if (p.HasItem(index))
            {
                return p;
            }
        }
        var startingIndex = (index / 1000) * 1000;
        var newPage = new Page(startingIndex, 1000);
        addPageToCache(newPage);
        return newPage;
    }

    private void addPageToCache(Page p)
    {
        if (pagesInMemory.Count > 4)
        {
            // remove oldest non-dirty page:
            var oldest = pagesInMemory
                .Where(page => !page.Dirty)
                .OrderBy(page => page.LastAccess)
                .FirstOrDefault();
            // Note that this may keep more than 5 pages in memory
            // if too much is dirty
            if (oldest != null)
                pagesInMemory.Remove(oldest);
        }
        pagesInMemory.Add(p);
    }
}

Tüm veri kümesini bellek içi bir koleksiyona yüklememek için iyi nedenler olan herhangi bir koleksiyon türünü modellemek için bu tasarım deyimini izleyebilirsiniz. sınıfının ortak arabirimin Page parçası olmayan özel bir iç içe geçmiş sınıf olduğuna dikkat edin. Bu ayrıntılar bu sınıfın tüm kullanıcılarından gizlenir.

Sözlükler

Bir diğer yaygın senaryo da bir sözlüğü veya haritayı modellemeniz gerektiği durumdur. Bu senaryo, türünüz değerleri genellikle metin anahtarları olan anahtara göre depoladığında güncelleştirilir. Bu örnek, komut satırı bağımsız değişkenlerini bu seçenekleri yöneten lambda ifadeleriyle eşleyen bir sözlük oluşturur. Aşağıdaki örnekte iki sınıf gösterilmektedir: ArgsActions bir komut satırı seçeneğini bir temsilciye eşleyen sınıf Action ve bu seçenekle karşılaştığında her Action birini yürütmek için öğesini ArgsActions kullanan bir ArgsProcessor sınıf.

public class ArgsProcessor
{
    private readonly ArgsActions actions;

    public ArgsProcessor(ArgsActions actions)
    {
        this.actions = actions;
    }

    public void Process(string[] args)
    {
        foreach(var arg in args)
        {
            actions[arg]?.Invoke();
        }
    }

}
public class ArgsActions
{
    readonly private Dictionary<string, Action> argsActions = new Dictionary<string, Action>();

    public Action this[string s]
    {
        get
        {
            Action action;
            Action defaultAction = () => {} ;
            return argsActions.TryGetValue(s, out action) ? action : defaultAction;
        }
    }

    public void SetOption(string s, Action a)
    {
        argsActions[s] = a;
    }
}

Bu örnekte koleksiyon, ArgsAction temel alınan koleksiyonla yakından eşler. , get belirli bir seçeneğin yapılandırılıp yapılandırılmadığını belirler. Öyleyse, bu seçenekle ilişkili değerini Action döndürür. Aksi takdirde, hiçbir şey içermeyen bir Action döndürür. Ortak erişimci bir set erişimci içermez. Bunun yerine, tasarım seçenekleri ayarlamak için genel bir yöntem kullanır.

Çok Boyutlu Haritalar

Birden çok bağımsız değişken kullanan dizin oluşturucular oluşturabilirsiniz. Ayrıca, bu bağımsız değişkenler aynı türde olacak şekilde kısıtlanmaz. şimdi iki örneğe bakalım.

İlk örnekte, Mandelbrot kümesi için değerler oluşturan bir sınıf gösterilmektedir. Kümenin arkasındaki matematik hakkında daha fazla bilgi için bu makaleyi okuyun. Dizin oluşturucu X, Y düzleminde bir nokta tanımlamak için iki çift kullanır. Get erişimcisi, bir noktanın kümede olmadığı belirlenene kadar yineleme sayısını hesaplar. En fazla yinelemeye ulaşılırsa, nokta kümededir ve sınıfın maxIterations değeri döndürülür. (Mandelbrot kümesi için popüler hale getirilen bilgisayar tarafından oluşturulan görüntüler, bir noktanın kümenin dışında olduğunu belirlemek için gereken yineleme sayısı için renkleri tanımlar.)

public class Mandelbrot
{
    readonly private int maxIterations;

    public Mandelbrot(int maxIterations)
    {
        this.maxIterations = maxIterations;
    }

    public int this [double x, double y]
    {
        get
        {
            var iterations = 0;
            var x0 = x;
            var y0 = y;

            while ((x*x + y * y < 4) &&
                (iterations < maxIterations))
            {
                var newX = x * x - y * y + x0;
                y = 2 * x * y + y0;
                x = newX;
                iterations++;
            }
            return iterations;
        }
    }
}

Mandelbrot Kümesi, gerçek sayı değerleri için her (x,y) koordinatta değerleri tanımlar. Bu, sonsuz sayıda değer içerebilen bir sözlüğü tanımlar. Bu nedenle, kümenin arkasında depolama alanı yoktur. Bunun yerine, kod erişimciyi çağırdığında get bu sınıf her nokta için değeri hesaplar. Kullanılan temel depolama alanı yoktur.

Dizin oluşturucunun farklı türlerde birden çok bağımsız değişken aldığı dizin oluşturucuların son bir kullanımını inceleyelim. Geçmiş sıcaklık verilerini yöneten bir program düşünün. Bu dizin oluşturucu, bu konumun yüksek ve düşük sıcaklıklarını ayarlamak veya almak için bir şehir ve tarih kullanır:

using DateMeasurements =
    System.Collections.Generic.Dictionary<System.DateTime, IndexersSamples.Common.Measurements>;
using CityDataMeasurements =
    System.Collections.Generic.Dictionary<string, System.Collections.Generic.Dictionary<System.DateTime, IndexersSamples.Common.Measurements>>;

public class HistoricalWeatherData
{
    readonly CityDataMeasurements storage = new CityDataMeasurements();

    public Measurements this[string city, DateTime date]
    {
        get
        {
            var cityData = default(DateMeasurements);

            if (!storage.TryGetValue(city, out cityData))
                throw new ArgumentOutOfRangeException(nameof(city), "City not found");

            // strip out any time portion:
            var index = date.Date;
            var measure = default(Measurements);
            if (cityData.TryGetValue(index, out measure))
                return measure;
            throw new ArgumentOutOfRangeException(nameof(date), "Date not found");
        }
        set
        {
            var cityData = default(DateMeasurements);

            if (!storage.TryGetValue(city, out cityData))
            {
                cityData = new DateMeasurements();
                storage.Add(city, cityData);
            }

            // Strip out any time portion:
            var index = date.Date;
            cityData[index] = value;
        }
    }
}

Bu örnek, iki farklı bağımsız değişkende hava durumu verilerini eşleyen bir dizin oluşturucu oluşturur: bir şehir (bir stringile temsil edilir) ve bir tarih (bir DateTimeile temsil edilir). İç depolama, iki Dictionary boyutlu sözlüğü temsil etmek için iki sınıf kullanır. Genel API artık temel alınan depolamayı temsil eder. Bunun yerine, dizin oluşturucuların dil özellikleri, temel alınan depolamanın farklı çekirdek koleksiyon türleri kullanması gerekse bile soyutlamanızı temsil eden bir ortak arabirim oluşturmanıza olanak tanır.

Bu kodun bazı geliştiricilere yabancı olabilecek iki bölümü vardır. Şu iki using yönerge:

using DateMeasurements = System.Collections.Generic.Dictionary<System.DateTime, IndexersSamples.Common.Measurements>;
using CityDataMeasurements = System.Collections.Generic.Dictionary<string, System.Collections.Generic.Dictionary<System.DateTime, IndexersSamples.Common.Measurements>>;

bir genel tür için bir diğer ad oluşturun. Bu deyimler kodun daha sonra ve genel yapısı Dictionary<DateTime, Measurements> yerine daha açıklayıcı DateMeasurements ve CityDataMeasurements adları kullanmasını Dictionary<string, Dictionary<DateTime, Measurements> >sağlar. Bu yapı, işaretin sağ tarafındaki = tam tür adlarının kullanılmasını gerektirir.

İkinci teknik, koleksiyonlarda dizin oluşturmak için kullanılan herhangi bir DateTime nesnenin zaman bölümlerini ayırmaktır. .NET yalnızca tarih türü içermez. Geliştiriciler türünü kullanırDateTime, ancak o güne ait herhangi bir DateTime nesnenin Date eşit olduğundan emin olmak için özelliğini kullanır.

Toplama

Sınıfınızda her özellik benzeri öğe olduğunda, bu özelliğin tek bir değeri değil, tek tek her öğenin bağımsız değişken kümesiyle tanımlandığı bir değer koleksiyonunu temsil ettiği dizin oluşturucular oluşturmanız gerekir. Bu bağımsız değişkenler koleksiyondaki hangi öğeye başvurulması gerektiğini benzersiz olarak tanımlayabilir. Dizin oluşturucular, bir üyenin sınıfın dışından bir veri öğesi gibi, ancak içindeki bir yöntem gibi ele alındığı özellikler kavramını genişletir. Dizin oluşturucular, bağımsız değişkenlerin bir öğe kümesini temsil eden bir özellikteki tek bir öğeyi bulmasına izin verir.