Aracılığıyla paylaş


Kayıt türleri oluşturma

Kayıtlar, değer tabanlı eşitlik kullanan türlerdir. Kayıtları başvuru türleri veya değer türleri olarak tanımlayabilirsiniz. Kayıt türü tanımları aynıysa ve her alan için her iki kayıttaki değerler eşitse, kayıt türünün iki değişkeni eşittir. Başvuruda bulunılan nesneler aynı sınıf türündeyse ve değişkenler aynı nesneye başvuruda bulunursa, sınıf türünün iki değişkeni eşittir. Değer tabanlı eşitlik, kayıt türlerinde büyük olasılıkla istediğiniz diğer özellikleri ifade eder. Derleyici, classyerine bir record bildirdiğinizde bu üyelerin çoğunu oluşturur. Derleyici, record struct türleri için aynı yöntemleri oluşturur.

Bu öğreticide aşağıdakilerin nasıl yapılacağını öğreneceksiniz:

  • record değiştiriciyi bir class türüne ekleyip eklemediğinize karar verin.
  • Kayıt türlerini ve konumsal kayıt türlerini bildirin.
  • Kayıtlarda derleyici tarafından oluşturulan yöntemleri yöntemlerinizle değiştirin.

Önkoşullar

Kayıtların özellikleri

record anahtar kelimesiyle bir tür bildirerek ve class veya struct bildirimini değiştirerek, kaydını olarak tanımlarsınız. İsteğe bağlı olarak, record classoluşturmak için class anahtar sözcüğünü atlayabilirsiniz. Kayıt, değer tabanlı eşitlik semantiğini izler. Değer semantiğini zorlamak için, derleyici kayıt türünüz için çeşitli yöntemler oluşturur (hem record class türleri hem de record struct türleri için):

Kayıtlar, Object.ToString()geçersiz kılma özelliğini de sunar. Derleyici, Object.ToString()kullanarak kayıtları görüntüleme yöntemlerini sentezler. Bu öğreticinin kodunu yazarken bu üyeleri keşfedersiniz. Kayıtlar, kayıtlar üzerinde yok edici olmayan mutasyon sağlamak için with ifadelerini destekler.

konumsal kayıtları daha kısa bir söz dizimi kullanarak da bildirebilirsiniz. Konumsal kayıtlar bildirdiğinizde derleyici sizin için daha fazla yöntem sentezler:

  • Parametreleri kayıt bildirimindeki konumsal parametrelerle eşleşen bir birincil oluşturucu.
  • Birincil oluşturucunun her parametresi için genel özellikler. Bu özellikler record class türleri ve readonly record struct türleri için yalnızca . record struct türleri için, okuma-yazma.
  • Kayıttan özellikleri ayıklamak için Deconstruct yöntemi.

Sıcaklık verileri oluşturma

Veriler ve istatistikler, kayıtları kullanmak istediğiniz senaryolar arasındadır. Bu öğreticide, farklı kullanımlar için derece gün hesaplayan bir uygulama oluşturacaksınız. Derece günleri, belirli bir süre içinde (günler, haftalar veya aylar) ısının (veya ısının eksikliğinin) bir ölçüsüdür. Derece günleri enerji kullanımını izler ve tahmin eder. Daha sıcak günler daha fazla klima anlamına gelir ve daha soğuk günler daha fazla fırın kullanımı anlamına gelir. Derece günleri, bitki popülasyonlarının yönetilmesine yardımcı olur ve mevsimler değiştikçe bitki büyümesiyle bağıntı sağlar. Derece günleri, iklime uygun seyahat eden türler için hayvan göçlerinin izlenmesine yardımcı olur.

Formül, belirli bir gündeki ortalama sıcaklığı ve taban çizgisi sıcaklığını temel alır. Zaman içindeki derece günlerini hesaplamak için her gün belirli bir süre boyunca yüksek ve düşük sıcaklığa ihtiyacınız olacaktır. Yeni bir uygulama oluşturarak başlayalım. Yeni bir konsol uygulaması oluşturun. "DailyTemperature.cs" adlı yeni bir dosyada yeni bir kayıt türü oluşturun:

public readonly record struct DailyTemperature(double HighTemp, double LowTemp);

Yukarıdaki kod, bir konumsal kayıt tanımlar. DailyTemperature kaydı, readonly record structbir kayıttır, çünkü bu kayıttan devralmayı düşünmediğiniz için değişmez olmalıdır. HighTemp ve LowTemp özellikleri yalnızca init özellikleridir; yani bunlar oluşturucuda veya özellik başlatıcı kullanılarak ayarlanabilir. Okuma-yazma olabilen konumsal parametreler istiyorsanız, readonly record structyerine record struct bildirirsiniz. DailyTemperature türü, iki özellik ile eşleşen iki parametreye sahip bir birincil oluşturucu'ye de sahiptir. DailyTemperature kaydı başlatmak için birincil oluşturucuyu kullanırsınız. Aşağıdaki kod, birkaç tane DailyTemperature kaydını oluşturur ve başlatır. İlki, HighTemp ve LowTempnetleştirmek için adlandırılmış parametreleri kullanır. Kalan başlatıcılar, HighTemp ve LowTemp'i konumsal parametrelerle başlatmak için kullanılır.

private static DailyTemperature[] data = [
    new DailyTemperature(HighTemp: 57, LowTemp: 30), 
    new DailyTemperature(60, 35),
    new DailyTemperature(63, 33),
    new DailyTemperature(68, 29),
    new DailyTemperature(72, 47),
    new DailyTemperature(75, 55),
    new DailyTemperature(77, 55),
    new DailyTemperature(72, 58),
    new DailyTemperature(70, 47),
    new DailyTemperature(77, 59),
    new DailyTemperature(85, 65),
    new DailyTemperature(87, 65),
    new DailyTemperature(85, 72),
    new DailyTemperature(83, 68),
    new DailyTemperature(77, 65),
    new DailyTemperature(72, 58),
    new DailyTemperature(77, 55),
    new DailyTemperature(76, 53),
    new DailyTemperature(80, 60),
    new DailyTemperature(85, 66) 
];

Konumsal kayıtlar dahil olmak üzere kayıtlara kendi özelliklerinizi veya yöntemlerinizi ekleyebilirsiniz. Her gün için ortalama sıcaklığı hesaplamanız gerekir. Bu özelliği DailyTemperature kaydına ekleyebilirsiniz:

public readonly record struct DailyTemperature(double HighTemp, double LowTemp)
{
    public double Mean => (HighTemp + LowTemp) / 2.0;
}

Şimdi bu verileri kullanabileceğinize emin olalım. Main yönteminize aşağıdaki kodu ekleyin:

foreach (var item in data)
    Console.WriteLine(item);

Uygulamanızı çalıştırdığınızda aşağıdaki görüntüye benzer bir çıktı görürsünüz (alan için birkaç satır kaldırıldı):

DailyTemperature { HighTemp = 57, LowTemp = 30, Mean = 43.5 }
DailyTemperature { HighTemp = 60, LowTemp = 35, Mean = 47.5 }


DailyTemperature { HighTemp = 80, LowTemp = 60, Mean = 70 }
DailyTemperature { HighTemp = 85, LowTemp = 66, Mean = 75.5 }

Yukarıdaki kod, derleyici tarafından sentezlenen ToString geçersiz kılma işleminin çıkışını gösterir. Farklı bir metin tercih ediyorsanız, derleyicinin sizin için bir sürümü sentezlemesini engelleyen kendi ToString sürümünüzü yazabilirsiniz.

Derece günlerini hesapla

Derece günlerini hesaplamak için, belirli bir gündeki temel sıcaklık ile ortalama sıcaklık arasındaki farkı alırsınız. Zaman içindeki ısıyı ölçmek için, ortalama sıcaklığın taban çizgisinin altında olduğu günleri atarsınız. Zaman içinde soğukluğu ölçmek için, ortalama sıcaklığın taban çizgisinin üzerinde olduğu günleri çıkartırsınız. Örneğin ABD, hem ısıtma hem de soğutma derecesi günleri için taban olarak 65 F kullanır. Bu, ısıtma veya soğutmanın gerekli olmadığı sıcaklıktır. Bir günün ortalama sıcaklığı 70 F ise, o gün beş soğutma derece günü ve sıfır ısıtma derecesi günüdür. Buna karşılık, ortalama sıcaklık 55 F ise, o gün 10 ısıtma derece günü ve 0 soğutma derecesi günüdür.

Bu formülleri küçük bir kayıt türleri hiyerarşisi olarak ifade edebilirsiniz: soyut bir derece gün türü ve ısıtma derecesi günleri ve soğutma derecesi günleri için iki somut tür. Bu türler konumsal kayıtlar da olabilir. Temel sıcaklık ve günlük sıcaklık kayıtlarını ana başlatıcının argümanları olarak alırlar.

public abstract record DegreeDays(double BaseTemperature, IEnumerable<DailyTemperature> TempRecords);

public sealed record HeatingDegreeDays(double BaseTemperature, IEnumerable<DailyTemperature> TempRecords)
    : DegreeDays(BaseTemperature, TempRecords)
{
    public double DegreeDays => TempRecords.Where(s => s.Mean < BaseTemperature).Sum(s => BaseTemperature - s.Mean);
}

public sealed record CoolingDegreeDays(double BaseTemperature, IEnumerable<DailyTemperature> TempRecords)
    : DegreeDays(BaseTemperature, TempRecords)
{
    public double DegreeDays => TempRecords.Where(s => s.Mean > BaseTemperature).Sum(s => s.Mean - BaseTemperature);
}

Soyut DegreeDays kaydı, hem HeatingDegreeDays hem de CoolingDegreeDays kayıtları için paylaşılan temel sınıftır. Türetilmiş kayıtlardaki birincil oluşturucu bildirimleri, temel kayıt başlatmanın nasıl yönetileceğini gösterir. Türetilmiş kaydınız, temel kaydın birincil oluşturucusundaki tüm parametreleri bildirir. Temel kayıt bu özellikleri bildirir ve başlatır. Türetilen kayıt bunları gizlemez, ancak yalnızca temel kaydında bildirilmeyen parametreler için özellikler oluşturur ve başlatır. Bu örnekte, türetilen kayıtlar yeni birincil oluşturucu parametreleri eklemez. Main yönteminize aşağıdaki kodu ekleyerek kodunuzu test edin:

var heatingDegreeDays = new HeatingDegreeDays(65, data);
Console.WriteLine(heatingDegreeDays);

var coolingDegreeDays = new CoolingDegreeDays(65, data);
Console.WriteLine(coolingDegreeDays);

Aşağıdaki gibi bir çıktı alırsınız:

HeatingDegreeDays { BaseTemperature = 65, TempRecords = record_types.DailyTemperature[], DegreeDays = 85 }
CoolingDegreeDays { BaseTemperature = 65, TempRecords = record_types.DailyTemperature[], DegreeDays = 71.5 }

Derleyici tarafından sentezlenen yöntemleri tanımlama

Kodunuz, bu süre boyunca doğru ısıtma ve soğutma derecesi gün sayısını hesaplar. Ancak bu örnekte, kayıtlar için sentezlenmiş yöntemlerden bazılarını neden değiştirmek isteyebileceğiniz gösterilmektedir. Kopyalama yöntemi dışında bir kayıt türünde derleyici tarafından sentezlenen yöntemlerden herhangi birinin kendi sürümünü bildirebilirsiniz. Kopyalama yönteminin derleyici tarafından oluşturulan bir adı vardır ve farklı bir uygulama sağlayamazsınız. Bu sentezlenen yöntemler arasında bir kopya oluşturucu, System.IEquatable<T> arabiriminin üyeleri, eşitlik ve eşitsizlik testleri ve GetHashCode()bulunur. Bu amaçla PrintMemberssentezlersiniz. Kendi ToString'ınızı da tanımlayabilirsiniz, ancak PrintMembers, devralma senaryoları için daha iyi bir seçenek sunar. Sentezlenmiş yöntemin kendi sürümünü sağlamak için imzanın sentezlenen yöntemle eşleşmesi gerekir.

Konsol çıkışındaki TempRecords öğesi kullanışlı değildir. Türünü görüntüler, ancak başka bir şeyi göstermez. Sentezlenmiş PrintMembers yöntemini kendi uygulamanızı sağlayarak bu davranışı değiştirebilirsiniz. İmza, record bildirimine uygulanan değiştiricilere bağlıdır:

  • Kayıt türü sealedveya record structise, imza private bool PrintMembers(StringBuilder builder);
  • Kayıt türü sealed değilse ve object'den türetiliyorsa (yani bir temel kayıt belirtmezse), imza protected virtual bool PrintMembers(StringBuilder builder);'dir.
  • Bir kayıt türü sealed değilse ve başka bir kayıttan türetiliyorsa, imza protected override bool PrintMembers(StringBuilder builder);

Bu kuralların anlaşılması en kolay yolu PrintMembersamacını anlamaktır. PrintMembers bir kayıt türündeki her özellik hakkında bilgileri dizeye ekler. Sözleşme, temel kayıtların üyelerini gösterime eklemesini gerektirir ve türetilmiş üyelerin de kendi üyelerini ekleyeceğini varsayar. Her kayıt türü, HeatingDegreeDaysiçin aşağıdaki örneğe benzer bir ToString geçersiz kılma sentezler:

public override string ToString()
{
    StringBuilder stringBuilder = new StringBuilder();
    stringBuilder.Append("HeatingDegreeDays");
    stringBuilder.Append(" { ");
    if (PrintMembers(stringBuilder))
    {
        stringBuilder.Append(" ");
    }
    stringBuilder.Append("}");
    return stringBuilder.ToString();
}

DegreeDays kaydında koleksiyonun türünü yazdırmayan bir PrintMembers yöntemi bildirirsiniz:

protected virtual bool PrintMembers(StringBuilder stringBuilder)
{
    stringBuilder.Append($"BaseTemperature = {BaseTemperature}");
    return true;
}

İmza, derleyicinin sürümüyle eşleşmesi için bir virtual protected yöntemi bildirir. Erişimcileri yanlış yaparsanız endişe etmeyin; dil doğru imzayı uygular. Sentezlenen herhangi bir yöntem için doğru değiştiricileri unutursanız, derleyici doğru imzayı almanıza yardımcı olacak uyarılar veya hatalar oluşturur.

ToString yöntemini bir kayıt türünde sealed olarak bildirebilirsiniz. Bu, türetilmiş kayıtların yeni bir uygulama sağlamasını engeller. Türetilmiş kayıtlar PrintMembers geçersiz kılmayı yine de içerecektir. Eğer kaydın çalışma zamanı türünü görüntülemesini istemeseydiniz, ToString'ı mühürlerdiniz. Yukarıdaki örnekte, kaydın ısıtma veya soğutma derecesi günlerini nerede ölçtdüğüne ilişkin bilgileri kaybedersiniz.

Yıkıcı olmayan mutasyon

Konumsal kayıt sınıfındaki sentezlenmiş üyeler kaydın durumunu değiştirmez. Amaç, sabit kayıtları daha kolay oluşturabilmenizdir. Sabit bir kayıt yapısı oluşturmak için bir readonly record struct bildirdiğiniz unutmayın. HeatingDegreeDays ve CoolingDegreeDaysiçin önceki bildirimlere yeniden bakın. Eklenen üyeler, kaydın değerleri üzerinde hesaplamalar gerçekleştirir, ancak durumu değiştirmezler. Konumsal kayıtlar sabit başvuru türleri oluşturmanızı kolaylaştırır.

Sabit başvuru türleri oluşturmak, yıkıcı olmayan mutasyon kullanmak istediğiniz anlamına gelir. with ifadelerini kullanarak mevcut kayıt örneklerine benzer yeni kayıt örnekleri oluşturursunuz. Bu ifadeler, kopyayı değiştiren ek atamaları olan bir kopya yapısıdır. Sonuç, her özelliğin mevcut kayıttan kopyalandığı ve isteğe bağlı olarak değiştirildiği yeni bir kayıt örneğidir. Özgün kayıt değiştirilmez.

Şimdi programınıza with ifadeleri gösteren birkaç özellik ekleyelim. İlk olarak, aynı verileri kullanarak artan dereceli günleri hesaplamak için yeni bir kayıt oluşturalım. Artan dereceli günler genellikle taban çizgisi olarak 41 F kullanır ve sıcaklıkları taban çizgisinin üzerinde ölçer. Aynı verileri kullanmak için, coolingDegreeDaysbenzer ancak farklı bir temel sıcaklığa sahip yeni bir kayıt oluşturabilirsiniz:

// Growing degree days measure warming to determine plant growing rates
var growingDegreeDays = coolingDegreeDays with { BaseTemperature = 41 };
Console.WriteLine(growingDegreeDays);

Hesaplanan derece sayısını, daha yüksek bir taban çizgisi sıcaklığıyla oluşturulan sayılarla karşılaştırabilirsiniz. Kayıtların başvuru türü olduğunu ve bu kopyaların basit kopyalar olduğunu unutmayın. Verilerin dizisi kopyalanmaz, ancak her iki kayıt da aynı verilere başvurur. Bu gerçek, başka bir senaryoda bir avantajdır. Artan derece gün sayısı için, önceki beş günün toplamını izlemek yararlı olur. with ifadeleri kullanarak farklı kaynak verilerle yeni kayıtlar oluşturabilirsiniz. Aşağıdaki kod bu birikimlerden oluşan bir koleksiyon oluşturur ve değerleri görüntüler:

// showing moving accumulation of 5 days using range syntax
List<CoolingDegreeDays> movingAccumulation = new();
int rangeSize = (data.Length > 5) ? 5 : data.Length;
for (int start = 0; start < data.Length - rangeSize; start++)
{
    var fiveDayTotal = growingDegreeDays with { TempRecords = data[start..(start + rangeSize)] };
    movingAccumulation.Add(fiveDayTotal);
}
Console.WriteLine();
Console.WriteLine("Total degree days in the last five days");
foreach(var item in movingAccumulation)
{
    Console.WriteLine(item);
}

Kayıtların kopyalarını oluşturmak için with ifadelerini de kullanabilirsiniz. with ifadesi için ayraçlar arasında hiçbir özellik belirtmeyin. Başka bir deyişle, bir kopya oluşturun ve hiçbir özelliği değiştirmeyin:

var growingDegreeDaysCopy = growingDegreeDays with { };

Sonuçları görmek için tamamlanmış uygulamayı çalıştırın.

Özet

Bu öğreticide kayıtların çeşitli yönleri gösterildi. Kayıtlar, temel kullanımın verileri depoladığı türler için kısa söz dizimi sağlar. Nesne odaklı sınıflar için temel kullanım sorumlulukları tanımlamaktır. Bu öğretici, konumsal kayıtlarüzerine odaklanıyor. Burada, kayıt özelliklerini bildirmek için kısa bir söz dizimi kullanabilirsiniz. Derleyici, kayıtları kopyalamak ve karşılaştırmak için kaydın birkaç üyesini sentezler. Kayıt türleriniz için ihtiyacınız olan diğer üyeleri ekleyebilirsiniz. Derleyici tarafından oluşturulan üyelerin hiçbirinin durumu değiştirmeyeceğini bilerek değişmez kayıt türleri oluşturabilirsiniz. ve with ifadeler, yıkıcı olmayan mutasyonu desteklemeyi kolaylaştırır.

Kayıtlar türleri tanımlamak için başka bir yol ekler. Nesnelerin sorumluluklarına ve davranışlarına odaklanan nesne odaklı hiyerarşiler oluşturmak için class tanımları kullanırsınız. Verileri depolayan ve verimli bir şekilde kopyalamak için yeterince küçük olan veri yapıları için struct türleri oluşturursunuz. Değer tabanlı eşitlik ve karşılaştırma istediğinizde, değerleri kopyalamak istemediğinizde ve başvuru değişkenlerini kullanmak istediğinizde record türleri oluşturursunuz. Verimli bir şekilde kopyalanacak kadar küçük bir tür için kayıtların özelliklerini istediğinizde record struct türleri oluşturursunuz.

Kayıt türü için C# dil başvurusu makalesinde kayıtlar hakkında daha fazla bilgi edinebilir ve önerilen kayıt türü belirtimi ve kayıt yapısı belirtimi.