Kayıt türleri oluşturma

Kayıtlar , değer tabanlı eşitlik kullanan türlerdir. C# 10, kayıtları değer türleri olarak tanımlayabilmeniz için kayıt yapıları ekler. 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 isteyeceğiniz diğer özellikleri ifade eder. Derleyici, yerine bir record bildirdiğinizde bu üyelerin classçoğunu oluşturur. Derleyici, türler için record struct aynı yöntemleri oluşturur.

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

  • Değiştiriciyi bir class türe record ekleyip eklemediğinize karar verin.
  • Kayıt türlerini ve konumsal kayıt türlerini bildirin.
  • Kayıtlarda derleyici tarafından oluşturulan yöntemler için yöntemlerinizi değiştirmeyin.

Ön koşullar

C# 10 veya üzeri derleyici de dahil olmak üzere makinenizi .NET 6 veya üzerini çalıştıracak şekilde ayarlamanız gerekir. C# 10 derleyicisi Visual Studio 2022 veya .NET 6 SDK'sı ile başlayarak kullanılabilir.

Kayıtların özellikleri

Bir veya bildirimini değiştirerek, anahtar sözcüğüyle record bir tür bildirerek bir classstruct kayıt tanımlarsınız. İsteğe bağlı olarak, oluşturmak record classiçin anahtar sözcüğünü class 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 türler hem record struct de türler içinrecord class):

Kayıtlar ayrıca geçersiz Object.ToString()kılma sağlar. Derleyici, kullanarak Object.ToString()kayıtları görüntülemek için yöntemleri sentezler. Bu öğreticinin kodunu yazarken bu üyeleri keşfedeceksiniz. Kayıtların yıkıcı olmayan mutasyona neden olması için ifadeleri destekler with .

Daha kısa bir söz dizimi kullanarak konumsal kayıtları 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 türler ve readonly record struct türler için record class yalnızca init'tir. Türler için record struct okuma-yazmadır.
  • Deconstruct Kayıttan özellikleri ayıklama yöntemi.

Sıcaklık verileri oluşturma

Veriler ve istatistikler, kayıtları kullanmak isteyeceğiniz senaryolar arasındadır. Bu öğreticide, farklı kullanımlar için derece günlerini hesaplayan bir uygulama oluşturacaksınız. Derece günleri , günler, haftalar veya aylar boyunca bir ısı ölçüsüdür (veya ısı eksikliği). Derece günleri enerji kullanımını izler ve tahmin edebilir. Daha sıcak günler daha fazla klima, daha soğuk günler ise 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. Kayıt DailyTemperature bir readonly record structolur, çünkü bu kayıttan devralmayı amaçlamazsınız ve sabit olmalıdır. HighTemp ve LowTemp özellikleri yalnızca init özellikleridir, yani oluşturucuda veya özellik başlatıcı kullanılarak ayarlanabilir. Konumsal parametrelerin okuma-yazma olmasını istiyorsanız, yerine bir record structreadonly record structbildirirsiniz. Türün DailyTemperature ayrıca, iki özellikle eşleşen iki parametresi olan bir birincil oluşturucu vardır. Bir kaydı başlatmak DailyTemperature için birincil oluşturucuyu kullanırsınız. Aşağıdaki kod birkaç DailyTemperature kayıt oluşturur ve başlatır. İlki, ve LowTemp'yi netleştirmek HighTemp için adlandırılmış parametreleri kullanır. Kalan başlatıcılar ve LowTemp'yi başlatmak HighTemp için konumsal parametreleri kullanı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 kayda 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. Yönteminize Main 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 geçersiz kılma işleminin ToString çıkışını gösterir. Farklı bir metin tercih ediyorsanız, derleyicinin ToString sizin için bir sürümü sentezlemesini engelleyen kendi sürümünüzü yazabilirsiniz.

İşlem derecesi günleri

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çindeki soğuk algınlıklarını ölçmek için, ortalama sıcaklığın taban çizgisinin üzerinde olduğu günleri atarsınız. Örneğin ABD, hem ısıtma hem de soğutma derecesi günlerinin tabanı olarak 65F kullanır. Bu, ısıtma veya soğutmanın gerekli olmadığı sıcaklıktır. Bir günün ortalama sıcaklığı 70F 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 55F 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ığı ve günlük sıcaklık kayıtlarını birincil oluşturucuya bağımsız değişken olarak alır:

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 kayıt, hem hem CoolingDegreeDays de HeatingDegreeDays 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 kayıt birincil oluşturucusunda tüm parametreler için 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. Yönteminize aşağıdaki kodu ekleyerek kodunuzu test edin Main :

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

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

Aşağıdaki ekran gibi bir çıkış 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 neden kayıtlar için sentezlenmiş yöntemlerden bazılarını 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 sentezlenmiş yöntemler arasında bir kopya oluşturucu, arabirimin System.IEquatable<T> üyeleri, eşitlik ve eşitsizlik testleri ve GetHashCode()bulunur. Bu amaçla sentezleyeceksiniz PrintMembers. Kendi öğesini ToStringde bildirebilirsiniz, ancak PrintMembers devralma senaryoları için daha iyi bir seçenek sağlar. Sentezlenmiş yöntemin kendi sürümünü sağlamak için imzanın sentezlenen yöntemle eşleşmesi gerekir.

TempRecords Konsol çıkışındaki öğesi kullanışlı değildir. Türü görüntüler, ancak başka bir şey görüntülemez. Sentezlenen PrintMembers yöntemin kendi uygulamasını sağlayarak bu davranışı değiştirebilirsiniz. İmza, bildirime uygulanan değiştiricilere record bağlıdır:

  • Bir kayıt türü sealedveya record structise, imzadır private bool PrintMembers(StringBuilder builder);
  • Bir kayıt türü değilse sealed ve türünden object türetilirse (yani, temel kayıt bildirmezse), imza şu şekildedir: protected virtual bool PrintMembers(StringBuilder builder);
  • Kayıt türü değilse sealed ve başka bir kayıttan türetiliyorsa, imza şu şekildedir: protected override bool PrintMembers(StringBuilder builder);

Bu kuralların amacını anlayarak anlaşılması en kolay yolu budur PrintMembers. PrintMembers bir dizeye kayıt türündeki her özellik hakkında bilgi ekler. Sözleşme, üyelerini görüntülemeye eklemek için temel kayıtları gerektirir ve türetilmiş üyelerin üyelerini ekleyeceğini varsayar. Her kayıt türü için aşağıdaki örneğe HeatingDegreeDaysbenzer 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();
}

Kayıtta koleksiyonun DegreeDays türünü yazdırmayan bir PrintMembers yöntem 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öntem bildirir. Aksesuarı yanlış alırsanız endişelenmeyin; dil, doğru imzayı zorlar. 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.

C# 10 ve sonraki sürümlerde, yöntemini bir kayıt türünde olduğu gibi sealed bildirebilirsinizToString. Bu, türetilmiş kayıtların yeni bir uygulama sağlamasını engeller. Türetilmiş kayıtlar yine de geçersiz kılmayı PrintMembers içerir. Kaydın çalışma zamanı türünü görüntülemesini istemiyorsanız mühürleyebilirsiniz ToString . 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 bildirdiğiniz readonly record struct unutmayın. ve CoolingDegreeDaysiçin HeatingDegreeDays önceki bildirimlere yeniden bakın. Eklenen üyeler, kaydın değerleri üzerinde hesaplamalar gerçekleştirir, ancak durumu sessize almaz. 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 isteyeceğiniz anlamına gelir. İfadeleri kullanarak with 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 ifadeleri gösteren with birkaç özellik ekleyelim. İlk olarak, aynı verileri kullanarak artan dereceli günleri hesaplamak için yeni bir kayıt oluşturalım. Artan derece günleri genellikle taban çizgisi olarak 41F kullanır ve sıcaklıkları taban çizgisinin üzerinde ölçer. Aynı verileri kullanmak için, öğesine benzer coolingDegreeDaysancak 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ürleri ve bu kopyaların basit kopyalar olduğunu unutmayın. Verilerin dizisi kopyalanır, 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. İfadeleri kullanarak with 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 ifadeleri de kullanabilirsiniz with . İfadenin ayraçları with 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, bir kaydın özelliklerini bildirmek için kısa bir söz dizimi kullanabileceğiniz konumsal kayıtlara odaklanmıştır. 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 sessize almayacağını bilerek sabit kayıt türleri oluşturabilirsiniz. İfadeler with yıkıcı olmayan mutasyonu desteklemeyi kolaylaştırır.

Kayıtlar türleri tanımlamak için başka bir yol ekler. Tanımları, nesnelerin sorumluluklarına ve davranışlarına odaklanan nesne odaklı hiyerarşiler oluşturmak için kullanırsınız class . Verileri depolayan ve verimli bir şekilde kopyalamak için yeterince küçük olan veri yapıları için türler oluşturursunuz struct . 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 türler oluşturursunuz record . Verimli bir şekilde kopyalanacak kadar küçük bir tür için kayıtların özelliklerini istediğinizde türler oluşturursunuz record struct .

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