Aracılığıyla paylaş


.NET 9 için .NET kitaplıklarındaki yenilikler

Bu makalede.NET 9 için .NET kitaplıklarındaki yeni özellikler açıklanmaktadır.

Temel64Url

Base64, rastgele baytları belirli bir 64 karakterden oluşan metne çeviren bir kodlama düzenidir. Bu, veri aktarımı için yaygın bir yaklaşımdır ve uzun zamandır Convert.ToBase64String veya Base64.DecodeFromUtf8(ReadOnlySpan<Byte>, Span<Byte>, Int32, Int32, Boolean) gibi çeşitli yöntemlerle desteklenmektedir. Ancak, kullandığı bazı karakterler, sorgu dizileri gibi durumlarda kullanılmasını tercih edebileceğiniz yerlerde ideal olmasını engeller. Özellikle, Base64 tablosunu oluşturan 64 karakterden ikisi de URL'lerde kendi anlamlarına sahip olan '+' ve '/' karakterlerini içerir. Bu, Base64'e benzer ancak URL'ler bağlamında kullanıma uygun hale getiren biraz farklı bir karakter kümesi kullanan Base64Url düzeninin oluşturulmasına yol açtı. .NET 9, çeşitli veri türleriyle Base64Url kodlama ve kod çözme için birçok yararlı ve iyileştirilmiş yöntem sağlayan yeni Base64Url sınıfı içerir.

Aşağıdaki örnekte yeni sınıfın kullanımı gösterilmektedir.

ReadOnlySpan<byte> bytes = ...;
string encoded = Base64Url.EncodeToString(bytes);

İkili Biçimlendirici

.NET 9, BinaryFormatter .NET çalışma zamanından kaldırır. API'ler hala mevcuttur, ancak uygulamaları proje türünden bağımsız olarak her zaman bir istisna fırlatır. Kaldırma işlemi ve etkileniyorsanız seçenekleriniz hakkında daha fazla bilgi almak için BinaryFormatter geçiş kılavuzu bölümüne bakın.

Koleksiyonlar

.NET'teki koleksiyon türleri .NET 9 için aşağıdaki güncelleştirmeleri alır:

Aralıklarla yapılan koleksiyon aramaları

Yüksek performanslı kodda span'lar genellikle dizeleri gereksiz yere ayırmamak için kullanılır ve gibi Dictionary<TKey,TValue>HashSet<T> türlerdeki arama tabloları genellikle önbellek olarak kullanılır. Ancak, bu koleksiyon türlerinde span'lerle arama yapmak için güvenli, yerleşik bir mekanizma mevcut değildir. C# 13'teki yeni allows ref struct özellik ve .NET 9'daki bu koleksiyon türlerindeki yeni özellikler sayesinde artık bu tür aramalar gerçekleştirilmesi mümkündür.

Aşağıdaki örnekte Dictionary<TKey,TValue kullanımı gösterilmektedir>. GetAlternateLookup.

static Dictionary<string, int> CountWords(ReadOnlySpan<char> input)
{
    Dictionary<string, int> wordCounts = new(StringComparer.OrdinalIgnoreCase);
    Dictionary<string, int>.AlternateLookup<ReadOnlySpan<char>> spanLookup =
        wordCounts.GetAlternateLookup<ReadOnlySpan<char>>();

    foreach (Range wordRange in Regex.EnumerateSplits(input, @"\b\W+"))
    {
        if (wordRange.Start.Value == wordRange.End.Value)
        {
            continue; // Skip empty ranges.
        }
        ReadOnlySpan<char> word = input[wordRange];
        spanLookup[word] = spanLookup.TryGetValue(word, out int count) ? count + 1 : 1;
    }

    return wordCounts;
}

OrderedDictionary<TKey, TValue>

Birçok senaryoda, anahtar-değer çiftlerini sıranın korunabileceği (anahtar-değer çiftlerinin listesi) ancak anahtara göre hızlı aramanın da desteklendiği (anahtar-değer çiftleri sözlüğü) bir şekilde depolamak isteyebilirsiniz. .NET'in ilk günlerinden bu yana, OrderedDictionary türü bu senaryoyu desteklemiştir, ancak yalnızca genel türde olmayan bir şekilde, anahtarlar ve değerler object türünde yazılmıştır. .NET 9, bu senaryoları desteklemek için verimli ve genel bir tür sağlayan, uzun zamandır beklenen OrderedDictionary<TKey,TValue> koleksiyonunu tanıtır.

Aşağıdaki kod yeni sınıfını kullanır.

OrderedDictionary<string, int> d = new()
{
    ["a"] = 1,
    ["b"] = 2,
    ["c"] = 3,
};

d.Add("d", 4);
d.RemoveAt(0);
d.RemoveAt(2);
d.Insert(0, "e", 5);

foreach (KeyValuePair<string, int> entry in d)
{
    Console.WriteLine(entry);
}

// Output:
// [e, 5]
// [b, 2]
// [c, 3]

PriorityQueue.Remove() yöntemi

.NET 6, basit ve hızlı bir dizi yığını uygulaması sağlayan koleksiyonu tanıttı PriorityQueue<TElement,TPriority> . Dizi yığınlarının genel olarak sorunlarından biri, öncelik güncelleştirmelerini desteklememeleridir ve bu da Dijkstra algoritmasının varyasyonları gibi algoritmalarda kullanılmasını yasaklar.

Mevcut koleksiyonda verimli $O(\log n)$ öncelik güncelleştirmeleri uygulamak mümkün olmasa da, yeni PriorityQueue<TElement,TPriority>.Remove(TElement, TElement, TPriority, IEqualityComparer<TElement>) yöntem öncelik güncelleştirmelerini öykünmenizi mümkün kılar ($O(n)$ zamanında da olsa):

public static void UpdatePriority<TElement, TPriority>(
    this PriorityQueue<TElement, TPriority> queue,
    TElement element,
    TPriority priority
    )
{
    // Scan the heap for entries matching the current element.
    queue.Remove(element, out _, out _);
    // Re-insert the entry with the new priority.
    queue.Enqueue(element, priority);
}

Bu yöntem, asimptotik performansın engelleyici olmadığı bağlamlarda grafik algoritmaları uygulamak isteyen kullanıcıların engelini kaldırır. (Bu tür bağlamlar eğitim ve prototip oluşturma içerir.) Örneğin, dijkstra algoritmasının yeni API'yi kullanan bir oyuncak uygulaması aşağıda verilmiştır.

ReadOnlySet<T>

Koleksiyonların salt okunur görünümlerini vermek genellikle tercih edilir. ReadOnlyCollection<T>, rastgele değiştirilebilir bir IList<T> içinde salt okunur bir sarmalayıcı oluşturmanıza olanak tanır ve ReadOnlyDictionary<TKey,TValue>, rastgele değiştirilebilir bir IDictionary<TKey,TValue> içinde salt okunur bir sarmalayıcı oluşturmanıza olanak tanır. Ancak, .NET'in önceki sürümlerinde aynı işlemi ile ISet<T>yapmak için yerleşik destek yoktu. .NET 9 bunu çözmek için ReadOnlySet<T> sunuyor.

Yeni sınıf aşağıdaki kullanım desenini etkinleştirir.

private readonly HashSet<int> _set = [];
private ReadOnlySet<int>? _setWrapper;

public ReadOnlySet<int> Set => _setWrapper ??= new(_set);

Bileşen modeli - TypeDescriptor kırpma desteği

System.ComponentModel bileşenleri tanımlamak için yeni, düzeltici uyumlu API'ler içerir. Özellikle kendi içinde kırpılan uygulamalar olmak üzere tüm uygulamalar, kırpma senaryolarını desteklemeye yardımcı olmak için bu yeni API'leri kullanabilir.

Birincil API, TypeDescriptor.RegisterType sınıfındaki TypeDescriptor yöntemidir. Bu yöntemin, düzelticinin söz konusu tür için üyeleri koruması amacıyla DynamicallyAccessedMembersAttribute özniteliği vardır. Bu yöntemi tür başına bir kez ve genellikle erken saatte çağırmanız gerekir.

İkincil API'lerin, FromRegisteredType gibi TypeDescriptor.GetPropertiesFromRegisteredType(Type) son ekleri vardır. Son eki FromRegisteredType olmayan karşılık gelenlerin aksine, bu API'lerin [RequiresUnreferencedCode] veya [DynamicallyAccessedMembers] düzeltici öznitelikleri yoktur. Düzeltici özniteliklerinin olmaması, tüketicilerin artık şunları yapmak zorunda kalmamasına yardımcı olur:

  • Riskli olabilecek kırpma uyarılarını gizleme.
  • Güçlü bir şekilde belirlenmiş Type bir parametreyi, zahmetli veya uygulanamaz olabilecek diğer yöntemlere yayma.
public static void RunIt()
{
    // The Type from typeof() is passed to a different method.
    // The trimmer doesn't know about ExampleClass anymore
    // and thus there will be warnings when trimming.
    Test(typeof(ExampleClass));
    Console.ReadLine();
}

private static void Test(Type type)
{
    // When publishing self-contained + trimmed,
    // this line produces warnings IL2026 and IL2067.
    PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(type);

    // When publishing self-contained + trimmed,
    // the property count is 0 here instead of 2.
    Console.WriteLine($"Property count: {properties.Count}");

    // To avoid the warning and ensure reflection
    // can see the properties, register the type:
    TypeDescriptor.RegisterType<ExampleClass>();
    // Get properties from the registered type.
    properties = TypeDescriptor.GetPropertiesFromRegisteredType(type);

    Console.WriteLine($"Property count: {properties.Count}");
}

public class ExampleClass
{
    public string? Property1 { get; set; }
    public int Property2 { get; set; }
}

Daha fazla bilgi için bkz. API teklifi.

Şifreleme

CryptographicOperations.HashData() yöntemi

.NET, karma işlevlerin ve ilgili işlevlerin birkaç statik "tek seferlik" uygulamasını içerir. Bu API'ler SHA256.HashData ve HMACSHA256.HashData içerir. Mümkün olan en iyi performansı sağlayıp ayırmaları azaltabilir veya ortadan kaldırabildiğinden, tek seferlik API'lerin kullanılması tercih edilir.

Geliştirici, çağıranın hangi hash algoritmasını kullanacağını tanımladığı bir hash oluşturmaya destek veren bir API sağlamak istiyorsa, bu genellikle bir HashAlgorithmName bağımsız değişken kabul ederek yapılır. Ancak, bu deseni tek kullanımlık API'lerle kullanmak için mümkün olan her HashAlgorithmName'ün değiştirilmesi ve ardından uygun yöntem kullanılması gerekir. Bu sorunu çözmek için .NET 9, API'yi tanıtır CryptographicOperations.HashData . Bu API, tek seferlik bir giriş üzerinde kullanılan algoritmanın HashAlgorithmName tarafından belirlendiği bir karma veya HMAC oluşturmanızı sağlar.

static void HashAndProcessData(HashAlgorithmName hashAlgorithmName, byte[] data)
{
    byte[] hash = CryptographicOperations.HashData(hashAlgorithmName, data);
    ProcessHash(hash);
}

KMAC algoritması

.NET 9, NIST SP-800-185 tarafından belirtilen KMAC algoritmasını sağlar. KECCAK İleti Kimlik Doğrulama Kodu (KMAC), KECCAK'ı temel alan bir yarı rastgele fonksiyon ve anahtarlı karma işlevidir.

Aşağıdaki yeni sınıflar KMAC algoritmasını kullanır. MAC oluşturmak için veri biriktirmek için örnekleri kullanın veya tek bir giriş üzerinde HashData için statik yöntemi kullanın.

KMAC, OpenSSL 3.0 veya üzeri yüklü Linux'ta ve Windows 11 Derleme 26016 veya sonraki sürümlerde kullanılabilir. Platformun istenen algoritmayı desteklenip desteklemediğini belirlemek için statik IsSupported özelliğini kullanabilirsiniz.

if (Kmac128.IsSupported)
{
    byte[] key = GetKmacKey();
    byte[] input = GetInputToMac();
    byte[] mac = Kmac128.HashData(key, input, outputLength: 32);
}
else
{
    // Handle scenario where KMAC isn't available.
}

iOS/tvOS/MacCatalyst için etkinleştirilen AES-GCM ve ChaChaPoly1305 algoritmaları

IsSupported ve ChaChaPoly1305.IsSupported şimdi iOS 13+, tvOS 13+ ve Mac Catalyst'te çalışırken true değerini döndürebilirsiniz.

AesGcm yalnızca Apple işletim sistemlerinde 16 bayt (128 bit) etiket değerlerini destekler.

X.509 sertifika yükleme

.NET Framework 2.0'dan bu yana, sertifika yüklemek için kullanılan yöntem new X509Certificate2(bytes) olmuştur. Ayrıca new X509Certificate2(bytes, password, flags), new X509Certificate2(path), new X509Certificate2(path, password, flags) ve X509Certificate2Collection.Import(bytes, password, flags) (ve onun aşırı yüklemeleri) gibi başka desenler de vardır.

Bu yöntemlerin tümü, girişin işleyebileceği bir şey olup olmadığını anlamak için içerik algılamayı kullandı ve eğer uygunsa yükledi. Bazı arayanlar için bu strateji çok uygundu. Ancak bazı sorunları da vardır:

  • Her dosya biçimi her işletim sisteminde çalışmaz.
  • Bu bir protokol sapması.
  • Bu, güvenlik sorunlarının kaynağıdır.

.NET 9, "tek yöntem, tek amaçlı" tasarıma sahip yeni X509CertificateLoader bir sınıf sunar. İlk sürümünde, oluşturucunun desteklediği beş biçimden X509Certificate2 yalnızca ikisini destekler. Bunlar tüm işletim sistemlerinde çalışan iki biçimdir.

OpenSSL sağlayıcıları desteği

.NET 8, OpenSSL'ye özgü API'leri OpenPrivateKeyFromEngine(String, String) ve OpenPublicKeyFromEngine(String, String)'yi kullanıma sunar. OpenSSL ENGINE bileşenleriyle etkileşime olanak tanır ve örneğin donanım güvenlik modüllerini (HSM) kullanır.

.NET 9, SafeEvpPKeyHandle.OpenKeyFromProvider(String, String)'ı tanıtarak OpenSSL sağlayıcılarının kullanılmasına ve tpm2 veya pkcs11 gibi sağlayıcılarla etkileşim kurmanıza olanak tanır.

Bazı dağıtımlar artık kullanım dışı olduğundan desteği kaldırmışENGINE.

Aşağıdaki kod parçacığında temel kullanım gösterilmektedir:

byte[] data = [ /* example data */ ];

// Refer to your provider documentation, for example, https://github.com/tpm2-software/tpm2-openssl/tree/master.
using (SafeEvpPKeyHandle priKeyHandle = SafeEvpPKeyHandle.OpenKeyFromProvider("tpm2", "handle:0x81000007"))
using (ECDsa ecdsaPri = new ECDsaOpenSsl(priKeyHandle))
{
    byte[] signature = ecdsaPri.SignData(data, HashAlgorithmName.SHA256);
    // Do stuff with signature created by TPM.
}

TLS el sıkışması sırasında bazı performans geliştirmelerinin yanı sıra bileşenleri kullanan ENGINE RSA özel anahtarlarıyla etkileşimlerde iyileştirmeler vardır.

Windows CNG sanallaştırma tabanlı güvenlik

Windows 11, sanallaştırma tabanlı güvenlik (VBS) ile Windows anahtarlarının güvenliğini sağlamaya yardımcı olmak için yeni API'ler ekledi. Bu yeni özellik sayesinde, anahtarlar performans, güvenilirlik veya ölçek üzerinde göz ardı edilebilir bir etkiyle yönetici düzeyinde anahtar hırsızlığı saldırılarına karşı korunabilir.

.NET 9 eşleşen CngKeyCreationOptions bayraklar ekledi. Aşağıdaki üç bayrak eklendi:

  • CngKeyCreationOptions.PreferVbs Eşleşen NCRYPT_PREFER_VBS_FLAG
  • CngKeyCreationOptions.RequireVbs Eşleşen NCRYPT_REQUIRE_VBS_FLAG
  • CngKeyCreationOptions.UsePerBootKey Eşleşen NCRYPT_USE_PER_BOOT_KEY_FLAG

Aşağıdaki kod parçacığında bayraklardan birinin nasıl kullanılacağı gösterilmektedir:

using System.Security.Cryptography;

CngKeyCreationParameters cngCreationParams = new()
{
    Provider = CngProvider.MicrosoftSoftwareKeyStorageProvider,
    KeyCreationOptions = CngKeyCreationOptions.RequireVbs | CngKeyCreationOptions.OverwriteExistingKey,
};

using (CngKey key = CngKey.Create(CngAlgorithm.ECDsaP256, "myKey", cngCreationParams))
using (ECDsaCng ecdsa = new ECDsaCng(key))
{
    // Do stuff with the key.
}

Tarih ve saat - yeni TimeSpan.From* aşırı yüklemeler

TimeSpan sınıfı, From* kullanarak bir TimeSpan nesnesi oluşturmanıza olanak tanıyan çeşitli double yöntemler sunar. Ancak, ikili tabanlı kayan nokta biçimi olduğundan double , doğal kararsızlık hatalara yol açabilir. Örneğin, TimeSpan.FromSeconds(101.832), tam olarak 101 seconds, 832 milliseconds'i temsil etmeyebilir, fakat yaklaşık olarak 101 seconds, 831.9999999999936335370875895023345947265625 milliseconds'yi temsil edebilir. Bu tutarsızlık sık sık karışıklığa neden olmuştur ve bu tür verileri temsil etmenin en verimli yolu da değildir. Bu sorunu çözmek için .NET 9, tamsayılardan nesne oluşturmanıza TimeSpan olanak sağlayan yeni aşırı yüklemeler ekler. FromDays, FromHours, FromMinutes, FromSeconds, FromMilliseconds ve FromMicroseconds için yeni aşırı yüklemeler vardır.

Aşağıdaki kod, double çağırmanın ve yeni tamsayı aşırı yüklemelerinden birinin bir örneğini gösterir.

TimeSpan timeSpan1 = TimeSpan.FromSeconds(value: 101.832);
Console.WriteLine($"timeSpan1 = {timeSpan1}");
// timeSpan1 = 00:01:41.8319999

TimeSpan timeSpan2 = TimeSpan.FromSeconds(seconds: 101, milliseconds: 832);
Console.WriteLine($"timeSpan2 = {timeSpan2}");
// timeSpan2 = 00:01:41.8320000

Bağımlılık ekleme - ActivatorUtilities.CreateInstance oluşturucu

.NET 9'da oluşturucu ActivatorUtilities.CreateInstance çözünürlüğü değişti. Daha önce, oluşturucuların sırasına ve oluşturucu parametrelerinin ActivatorUtilitiesConstructorAttribute sayısına bağlı olarak özniteliği kullanılarak açıkça işaretlenen bir oluşturucu çağrılamayabilir. .NET 9'da mantık, özelliğe sahip bir oluşturucunun her zaman çağrılması için değişmiştir.

Tanılama

Debug.Assert varsayılan olarak onay koşulunu raporlar

Debug.Assert genellikle her zaman doğru olması beklenen koşulları doğrulamaya yardımcı olmak için kullanılır. Hata genellikle koddaki bir hatayı gösterir. Debug.Assert'un birçok aşırı yüklemesi vardır, en basit olanı yalnızca bir koşulu kabul eder.

Debug.Assert(a > 0 && b > 0);

Koşul yanlışsa doğrulama başarısız olur. Ancak geçmişte bu tür iddialar, hangi koşulun başarısız olduğuna dair hiçbir bilgi içermiyordu. .NET 9'dan başlayarak, kullanıcı tarafından açıkça hiçbir ileti sağlanmazsa onay, koşulun metinsel gösterimini içerir. Örneğin, aşağıdaki gibi bir ileti almak yerine önceki onay örneği için:

Process terminated. Assertion failed.
   at Program.SomeMethod(Int32 a, Int32 b)

İleti şu şekilde olacaktır:

Process terminated. Assertion failed.
a > 0 && b > 0
   at Program.SomeMethod(Int32 a, Int32 b)

Daha önce, bir izlemeyi Activity yalnızca öğesini oluşturduğunuzda Activitydiğer izleme bağlamlarına bağlayabilirsiniz. .NET 9'da yeni eklenen AddLink(ActivityLink) API, bir Activity nesneyi oluşturulduktan sonra diğer izleme bağlamlarına bağlamanıza olanak tanır. Bu değişiklik OpenTelemetry belirtimleriyle de uyumlu hale geldi.

ActivityContext activityContext = new(ActivityTraceId.CreateRandom(), ActivitySpanId.CreateRandom(), ActivityTraceFlags.None);
ActivityLink activityLink = new(activityContext);

Activity activity = new("LinkTest");
activity.AddLink(activityLink);

Metrics.Gauge ölçüm aleti

System.Diagnostics.Metrics şimdi Gauge<T> aracını OpenTelemetry belirtimine göre sağlar. Araç Gauge , değişiklikler gerçekleştiğinde eksiz değerleri kaydedecek şekilde tasarlanmıştır. Örneğin, birden çok odadan gelen değerlerin toplanmasının mantıksız olacağı arka plan gürültüsü düzeyini ölçebilir. Enstrüman Gauge, int gibi herhangi bir değer türünü kaydedebilen genel bir türdür, örneğin double veya decimal.

Aşağıdaki örnek, bu Gauge aracının kullanımını göstermektedir.

Meter soundMeter = new("MeasurementLibrary.Sound");
Gauge<int> gauge = soundMeter.CreateGauge<int>(
    name: "NoiseLevel",
    unit: "dB", // Decibels.
    description: "Background Noise Level"
    );
gauge.Record(10, new TagList() { { "Room1", "dB" } });

Harici süreç sayaç joker karakteri dinleme

Olay kaynağı sağlayıcısını kullanarak System.Diagnostics.Metrics işlem dışı ölçümleri dinlemek zaten mümkündür, ancak .NET 9'un öncesinde tam ölçüm adını belirtmeniz gerekiyordu. .NET 9'da, bir işlemdeki her ölçümden ölçüm yakalamanızı sağlayan joker karakterini *kullanarak tüm ölçümleri dinleyebilirsiniz. Ayrıca ölçüm ön ekine göre dinleme desteği ekler, böylece adları belirtilen ön ekle başlayan tüm ölçümleri dinleyebilirsiniz. Örneğin, MyMeter* belirterek, MyMeter ile başlayan adlara sahip tüm ölçümleri dinlemeyi sağlar.

// The complete meter name is "MyCompany.MyMeter".
var meter = new Meter("MyCompany.MyMeter");
// Create a counter and allow publishing values.
meter.CreateObservableCounter("MyCounter", () => 1);

// Create the listener to use the wildcard character
// to listen to all meters using prefix names.
MyEventListener listener = new MyEventListener();

MyEventListener sınıfı aşağıdaki gibi tanımlanır.

internal class MyEventListener : EventListener
{
    protected override void OnEventSourceCreated(EventSource eventSource)
    {
        Console.WriteLine(eventSource.Name);
        if (eventSource.Name == "System.Diagnostics.Metrics")
        {
            // Listen to all meters with names starting with "MyCompany".
            // If using "*", allow listening to all meters.
            EnableEvents(
                eventSource,
                EventLevel.Informational,
                (EventKeywords)0x3,
                new Dictionary<string, string?>() { { "Metrics", "MyCompany*" } }
                );
        }
    }

    protected override void OnEventWritten(EventWrittenEventArgs eventData)
    {
        // Ignore other events.
        if (eventData.EventSource.Name != "System.Diagnostics.Metrics" ||
            eventData.EventName == "CollectionStart" ||
            eventData.EventName == "CollectionStop" ||
            eventData.EventName == "InstrumentPublished"
            )
            return;

        Console.WriteLine(eventData.EventName);

        if (eventData.Payload is not null)
        {
            for (int i = 0; i < eventData.Payload.Count; i++)
                Console.WriteLine($"\t{eventData.PayloadNames![i]}: {eventData.Payload[i]}");
        }
    }
}

Kodu yürütürken çıkış aşağıdaki gibidir:

CounterRateValuePublished
        sessionId: 7cd94a65-0d0d-460e-9141-016bf390d522
        meterName: MyCompany.MyMeter
        meterVersion:
        instrumentName: MyCounter
        unit:
        tags:
        rate: 0
        value: 1
        instrumentId: 1
CounterRateValuePublished
        sessionId: 7cd94a65-0d0d-460e-9141-016bf390d522
        meterName: MyCompany.MyMeter
        meterVersion:
        instrumentName: MyCounter
        unit:
        tags:
        rate: 0
        value: 1
        instrumentId: 1

Dotnet-counters gibi izleme araçlarıyla ölçümleri dinlemek için joker karakteri de kullanabilirsiniz.

LINQ

Yeni yöntemler CountBy ve AggregateBy tanıtılmıştır. Bu yöntemler aracılığıyla GroupBy kullanılarak ara gruplandırmalara ayırmaya gerek kalmadan durumu anahtara göre toplamayı mümkün hale getirir.

CountBy her anahtarın sıklığını hızlı bir şekilde hesaplamanızı sağlar. Aşağıdaki örnek, bir metin dizesinde en sık oluşan sözcüğü bulur.

string sourceText = """
    Lorem ipsum dolor sit amet, consectetur adipiscing elit.
    Sed non risus. Suspendisse lectus tortor, dignissim sit amet, 
    adipiscing nec, ultricies sed, dolor. Cras elementum ultrices amet diam.
""";

// Find the most frequent word in the text.
KeyValuePair<string, int> mostFrequentWord = sourceText
    .Split(new char[] { ' ', '.', ',' }, StringSplitOptions.RemoveEmptyEntries)
    .Select(word => word.ToLowerInvariant())
    .CountBy(word => word)
    .MaxBy(pair => pair.Value);

Console.WriteLine(mostFrequentWord.Key); // amet

AggregateBy daha genel amaçlı iş akışları uygulamanıza olanak tanır. Aşağıdaki örnek, belirli bir anahtarla ilişkili puanları nasıl hesaplayabileceğinizi gösterir.

(string id, int score)[] data =
    [
        ("0", 42),
        ("1", 5),
        ("2", 4),
        ("1", 10),
        ("0", 25),
    ];

var aggregatedData =
    data.AggregateBy(
        keySelector: entry => entry.id,
        seed: 0,
        (totalScore, curr) => totalScore + curr.score
        );

foreach (var item in aggregatedData)
{
    Console.WriteLine(item);
}
//(0, 67)
//(1, 15)
//(2, 4)

Index<TSource>(IEnumerable<TSource>) , numaralandırılabilir bir dizinin örtük dizinini hızlı bir şekilde ayıklamayı mümkün kılar. Artık bir koleksiyondaki öğeleri otomatik olarak dizine almak için aşağıdaki kod parçacığı gibi kod yazabilirsiniz.

IEnumerable<string> lines2 = File.ReadAllLines("output.txt");
foreach ((int index, string line) in lines2.Index())
{
    Console.WriteLine($"Line number: {index + 1}, Line: {line}");
}

Günlüğe kaydetme kaynak oluşturucu

C# 12, doğrudan sınıf bildiriminde bir oluşturucu tanımlamanızı sağlayan birincil oluşturucuları kullanıma sunar. Günlük kaynağı oluşturucu artık ana yapıcıya sahip sınıfları kullanarak günlüğe kaydetmeyi destekliyor.

public partial class ClassWithPrimaryConstructor(ILogger logger)
{
    [LoggerMessage(0, LogLevel.Debug, "Test.")]
    public partial void Test();
}

Çeşitli

Bu bölümde, aşağıdakiler hakkında bilgi bulabilirsiniz:

allows ref struct kitaplıklarda kullanılır

C# 13, derleyiciye ve çalışma zamanına bir genel parametreyi allows ref struct ile sınırlayabileceğini ve o genel parametre için bir ref struct kullanabileceğini bildiren özelliği tanıtır. Bununla uyumlu birçok API'ye artık açıklama eklenmiştir. Örneğin, bu String.Create yöntemi, doğrudan belleğine yazarak bir span olarak gösterilen bir string yaratmanız için bir fazladan yükü var. Bu yöntemin, çağırandan temsilciye geçirilen ve gerçek yazmayı sağlayan bir TState bağımsız değişkeni vardır.

TState üzerindeki String.Create türü parametresi artık allows ref struct ile açıklanıyor:

public static string Create<TState>(int length, TState state, SpanAction<char, TState> action)
    where TState : allows ref struct;

Bu açıklama, bu yönteme girdi olarak bir span (veya bir başka ref struct) geçirmenizi sağlar.

Aşağıdaki örnekte bu özelliği kullanan yeni String.ToLowerInvariant() bir aşırı yükleme gösterilmektedir.

public static string ToLowerInvariant(ReadOnlySpan<char> input) =>
    string.Create(span.Length, input, static (stringBuffer, input) => span.ToLowerInvariant(stringBuffer));

SearchValues genleşme

.NET 8, spanler içindeki belirli karakter veya bayt kümelerini aramak için iyileştirilmiş bir çözüm sağlayan SearchValues<T> türünü tanıttı. .NET 9'da, SearchValues daha büyük bir dize içinde alt dizeleri aramayı destekleyecek şekilde genişletilmiştir.

Aşağıdaki örnek, bir dize değeri içinde birden çok hayvan adı arar ve bulunan ilk değere bir dizin döndürür.

private static readonly SearchValues<string> s_animals =
    SearchValues.Create(["cat", "mouse", "dog", "dolphin"], StringComparison.OrdinalIgnoreCase);

public static int IndexOfAnimal(string text) =>
    text.AsSpan().IndexOfAny(s_animals);

Bu yeni özellik, temel platformdaki SIMD desteğinden yararlanan iyileştirilmiş bir uygulamaya sahiptir. Ayrıca, daha üst düzey türlerin en iyi duruma getirilmeye olanak tanır. Örneğin, Regex şimdi bu işlevi uygulamanın bir parçası olarak kullanıyor.

Ağ Kurma

HttpClientFactory'de SocketsHttpHandler varsayılandır

HttpClientFactory, varsayılan olarak HttpClient tarafından desteklenen HttpClientHandler nesneleri oluşturur. HttpClientHandler, bağlantı ömrü yönetimi de dahil olmak üzere çok daha yapılandırılabilir olan SocketsHttpHandler tarafından desteklenmiştir. HttpClientFactory artık varsayılan olarak SocketsHttpHandler kullanıyor ve bağlantı ömrü sınırlarını fabrika içinde belirtilen döndürme ömrüyle eşleşecek şekilde ayarlamak için yapılandırıyor.

System.Net.ServerSentEvents

Sunucu tarafından gönderilen olaylar (SSE), bir sunucudan istemciye veri akışı yapmak için kullanılan basit ve popüler bir protokoldür. Örneğin OpenAI tarafından yapay zeka hizmetlerinden oluşturulan metin akışının bir parçası olarak kullanılır. SSE tüketimini basitleştirmek için yeni System.Net.ServerSentEvents kitaplık, sunucu tarafından gönderilen olayları kolayca almak için bir ayrıştırıcı sağlar.

Aşağıdaki kod, yeni sınıfın kullanılmasını gösterir.

Stream responseStream = new MemoryStream();
await foreach (SseItem<string> e in SseParser.Create(responseStream).EnumerateAsync())
{
    Console.WriteLine(e.Data);
}

Linux'ta istemci sertifikaları ile TLS özgeçmişi

TLS özgeçmişi , daha önce oluşturulmuş oturumların bir sunucuda sürdürülmesini sağlayan bir TLS protokolü özelliğidir. Bunu yapmak, bir kaç turdan ve TLS el sıkışması sırasında hesaplama kaynaklarından tasarruf edilmesini sağlar.

SslStream bağlantıları için istemci sertifikaları olmadan Linux üzerinde TLS oturum devamı zaten destekleniyordu. .NET 9, sunucudan sunucuya senaryolarda yaygın olarak kullanılan karşılıklı kimliği doğrulanmış TLS bağlantılarının TLS özgeçmişi için destek ekler. Özellik otomatik olarak etkinleştirilir.

WebSocket devamlılık, ping ve zaman aşımı

Yeni API'ler ClientWebSocketOptions ve WebSocketCreationOptions üzerinde, eş zamanında yanıt alınmazsa WebSocket pingi göndermeyi ve bağlantıyı durdurmayı seçebilmenize olanak tanır.

Şimdiye kadar, bağlantının boşta kalmaması için bir KeepAliveInterval belirtebilirdiniz, ancak karşı tarafın yanıt verdiğini zorunlu kılacak yerleşik bir mekanizma yoktu.

Aşağıdaki örnek, sunucuya her 5 saniyede bir ping gönderir ve bir saniye içinde yanıt vermezse bağlantıyı durdurur.

using var cws = new ClientWebSocket();
cws.Options.HttpVersionPolicy = HttpVersionPolicy.RequestVersionOrHigher;
cws.Options.KeepAliveInterval = TimeSpan.FromSeconds(5);
cws.Options.KeepAliveTimeout = TimeSpan.FromSeconds(1);

await cws.ConnectAsync(uri, httpClient, cancellationToken);

HttpClientFactory artık üst bilgi değerlerini varsayılan olarak günlüğe kaydetmemektedir

Artık, LogLevel.Trace tarafından HttpClientFactory günlüğe kaydedilen olaylar varsayılan olarak üst bilgi değerlerini içermez. Yardımcı yöntem RedactLoggedHeaders aracılığıyla belirli üst bilgilerin kayıt değerlerini etkinleştirebilirsiniz.

Aşağıdaki örnek, kullanıcı aracısı dışında tüm üst bilgileri sansürler.

services.AddHttpClient("myClient")
    .RedactLoggedHeaders(name => name != "User-Agent");

Daha fazla bilgi için bkz. HttpClientFactory günlüğü üst bilgi değerlerini varsayılan olarak gizler.

Yansıma

Kalıcı derlemeler

.NET Core ve .NET 5-8 sürümlerinde, bir derleme oluşturma ve dinamik olarak oluşturulan türler için yansıma meta verilerini yayma desteği, yalnızca çalıştırılabilir bir AssemblyBuilder ile sınırlıydı. Bir derlemenin kaydedilmesine destek olmaması, genellikle .NET Framework'ten .NET'e geçiş yapan müşteriler için bir engeldi. .NET 9, PersistedAssemblyBuilderyayılan bir derlemeyi kaydetmek için kullanabileceğiniz yeni bir tür sunar.

Bir PersistedAssemblyBuilder örnek oluşturmak için oluşturucusunu çağırın ve temel çalışma zamanı türlerine ve isteğe bağlı özel özniteliklere başvurmak için derleme adını, System.Private.CoreLibçekirdek derlemesini , geçirin. Tüm üyeleri derlemeye yaydıktan sonra, PersistedAssemblyBuilder.Save(String) yöntemini varsayılan ayarlarla bir derleme oluşturmak için çağırın. Giriş noktasını veya diğer seçenekleri ayarlamak istiyorsanız, derlemeyi kaydetmek için döndürdüğü meta verileri çağırabilir PersistedAssemblyBuilder.GenerateMetadata ve kullanabilirsiniz. Aşağıdaki kodda kalıcı derleme oluşturma ve giriş noktasını ayarlama örneği gösterilmektedir.

public void CreateAndSaveAssembly(string assemblyPath)
{
    PersistedAssemblyBuilder ab = new PersistedAssemblyBuilder(
        new AssemblyName("MyAssembly"),
        typeof(object).Assembly
        );
    TypeBuilder tb = ab.DefineDynamicModule("MyModule")
        .DefineType("MyType", TypeAttributes.Public | TypeAttributes.Class);

    MethodBuilder entryPoint = tb.DefineMethod(
        "Main",
        MethodAttributes.HideBySig | MethodAttributes.Public | MethodAttributes.Static
        );
    ILGenerator il = entryPoint.GetILGenerator();
    // ...
    il.Emit(OpCodes.Ret);

    tb.CreateType();

    MetadataBuilder metadataBuilder = ab.GenerateMetadata(
        out BlobBuilder ilStream,
        out BlobBuilder fieldData
        );
    PEHeaderBuilder peHeaderBuilder = new PEHeaderBuilder(
                    imageCharacteristics: Characteristics.ExecutableImage);

    ManagedPEBuilder peBuilder = new ManagedPEBuilder(
                    header: peHeaderBuilder,
                    metadataRootBuilder: new MetadataRootBuilder(metadataBuilder),
                    ilStream: ilStream,
                    mappedFieldData: fieldData,
                    entryPoint: MetadataTokens.MethodDefinitionHandle(entryPoint.MetadataToken)
                    );

    BlobBuilder peBlob = new BlobBuilder();
    peBuilder.Serialize(peBlob);

    using var fileStream = new FileStream("MyAssembly.exe", FileMode.Create, FileAccess.Write);
    peBlob.WriteContentTo(fileStream);
}

public static void UseAssembly(string assemblyPath)
{
    Assembly assembly = Assembly.LoadFrom(assemblyPath);
    Type? type = assembly.GetType("MyType");
    MethodInfo? method = type?.GetMethod("SumMethod");
    Console.WriteLine(method?.Invoke(null, [5, 10]));
}

Yeni PersistedAssemblyBuilder sınıf PDB desteği içerir. Sembol bilgilerini yayabilir ve oluşturulan derlemede hata ayıklamak için kullanabilirsiniz. API, .NET Framework uygulamasına benzer bir şekle sahiptir. Daha fazla bilgi için bkz . Sembolleri yayma ve PDB oluşturma.

Tür adı ayrıştırma

TypeName, çalışma zamanı ortamından bağımsız olması dışında System.Type ile benzer işlevsellik sunan, ECMA-335 tür adları için bir ayrıştırıcıdır. Seri hale getiriciler ve derleyiciler gibi bileşenlerin tür adlarını ayrıştırıp işlemesi gerekir. Örneğin, Yerel AOT derleyicisi TypeName kullanmaya geçti.

Yeni TypeName sınıf aşağıdakileri sağlar:

  • Statik Parse ve TryParse olarak ReadOnlySpan<char>temsil edilen girişi ayrıştırma yöntemleri. Her iki yöntem de ayrıştırma özelleştirmenizi sağlayan bir sınıf örneğini TypeNameParseOptions (seçenek paketi) kabul eder.

  • Name, FullName, ve AssemblyQualifiedName özellikleri, System.Type'deki karşılıkları gibi çalışır.

  • Adın kendisi hakkında ek bilgi sağlayan birden çok özellik ve yöntem:

    • IsArray, IsSZArray (SZ tek boyutlu, sıfır dizinli dizi) IsVariableBoundArrayTypeve GetArrayRank dizilerle çalışmayı ifade eder.
    • IsConstructedGenericType, GetGenericTypeDefinitionve GetGenericArguments genel tür adlarıyla çalışmak için.
    • IsByRef ve IsPointer işaretçiler ve yönetilen başvurularla çalışmak için.
    • GetElementType() işaretçiler, referanslar ve dizilerle çalışmak için.
    • IsNested ve DeclaringType iç içe türlerle çalışmak için.
    • AssemblyName, derleme adı bilgilerini yeni AssemblyNameInfo sınıf aracılığıyla kullanıma sunar. Aksine AssemblyName, yeni tür değişmezdir ve kültür adlarını ayrıştırmak örnek CultureInfoyaratmaz.

Hem TypeName hem de AssemblyNameInfo türleri değişmezdir ve eşitliği denetlemek için bir yol sağlamaz (uygulanmaz IEquatable). Derleme adlarını karşılaştırmak basittir, ancak farklı senaryoların yalnızca kullanıma sunulan bilgilerin (Name, Version, CultureNameve PublicKeyOrToken) bir alt kümesini karşılaştırması gerekir.

Aşağıdaki kod parçacığında bazı örnek kullanımlar gösterilmektedir.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection.Metadata;

internal class RestrictedSerializationBinder
{
    Dictionary<string, Type> AllowList { get; set; }

    RestrictedSerializationBinder(Type[] allowedTypes)
        => AllowList = allowedTypes.ToDictionary(type => type.FullName!);

    Type? GetType(ReadOnlySpan<char> untrustedInput)
    {
        if (!TypeName.TryParse(untrustedInput, out TypeName? parsed))
        {
            throw new InvalidOperationException($"Invalid type name: '{untrustedInput.ToString()}'");
        }

        if (AllowList.TryGetValue(parsed.FullName, out Type? type))
        {
            return type;
        }
        else if (parsed.IsSimple // It's not generic, pointer, reference, or an array.
            && parsed.AssemblyName is not null
            && parsed.AssemblyName.Name == "MyTrustedAssembly"
            )
        {
            return Type.GetType(parsed.AssemblyQualifiedName, throwOnError: true);
        }

        throw new InvalidOperationException($"Not allowed: '{untrustedInput.ToString()}'");
    }
}

Yeni API'ler, alt düzey .NET sürümleriyle kullanılabilen NuGet paketinden System.Reflection.Metadata kullanılabilir.

Normal ifadeler

[GeneratedRegex] özellikler üzerinde

.NET 7, kaynak oluşturucuyu Regex ve buna karşılık gelen GeneratedRegexAttribute özniteliğini kullanıma sunar.

Aşağıdaki kısmi yöntem, bu Regex öğesini uygulamak için gereken tüm kod ile kaynak tarafından oluşturulacaktır.

[GeneratedRegex(@"\b\w{5}\b")]
private static partial Regex FiveCharWord();

C# 13, kısmi yöntemlere ek olarak kısmi özellikleri de destekler, bu nedenle .NET 9'dan başlayarak bir özellikte de kullanabilirsiniz [GeneratedRegex(...)] .

Aşağıdaki kısmi özellik, önceki örneğin özellik eşdeğeridir.

[GeneratedRegex(@"\b\w{5}\b")]
private static partial Regex FiveCharWordProperty { get; }

Regex.EnumerateSplits

Regex sınıfı, kavram olarak yöntemine Split benzer bir String.Split yöntem sağlar. ile String.Splitbir veya daha fazla char veya string ayırıcı sağlarsınız ve uygulama giriş metnini bu ayırıcılara böler. Regex.Split ile, ayırıcıyı bir char veya string olarak belirtmek yerine, normal ifade deseni olarak belirtilir.

Aşağıdaki örnekte gösterilmiştir Regex.Split.

foreach (string s in Regex.Split("Hello, world! How are you?", "[aeiou]"))
{
    Console.WriteLine($"Split: \"{s}\"");
}

// Output, split by all English vowels:
// Split: "H"
// Split: "ll"
// Split: ", w"
// Split: "rld! H"
// Split: "w "
// Split: "r"
// Split: " y"
// Split: ""
// Split: "?"

Ancak, Regex.Split yalnızca bir string 'i giriş olarak kabul eder ve giriş olarak ReadOnlySpan<char> sağlanmasını desteklemez. Ayrıca, tam bölme kümesini bir string[] olarak verir. Bu, sonuçları tutmak için hem string dizisini hem de her bölme için bir string ayırmayı gerektirir. .NET 9'da, yeni EnumerateSplits yöntem aynı işlemi gerçekleştirmeye olanak tanır, ancak span tabanlı bir girişle ve sonuçlar için herhangi bir ayırma yapılmadan. ReadOnlySpan<char> kabul eder ve sonuçları temsil eden nesnelerden oluşan numaralandırılabilir bir Range döndürür.

Aşağıdaki örnek Regex.EnumerateSplits kullanarak, giriş olarak bir ReadOnlySpan<char> değeri almayı göstermektedir.

ReadOnlySpan<char> input = "Hello, world! How are you?";
foreach (Range r in Regex.EnumerateSplits(input, "[aeiou]"))
{
    Console.WriteLine($"Split: \"{input[r]}\"");
}

Serileştirme (System.Text.Json)

Girinti seçenekleri

JsonSerializerOptions , yazılmış JSON'un girinti karakterini ve girinti boyutunu özelleştirmenize olanak sağlayan yeni özellikler içerir.

var options = new JsonSerializerOptions
{
    WriteIndented = true,
    IndentCharacter = '\t',
    IndentSize = 2,
};

string json = JsonSerializer.Serialize(
    new { Value = 1 },
    options
    );
Console.WriteLine(json);
//{
//                "Value": 1
//}

Varsayılan web seçenekleri tekil

ASP.NET Core'un web uygulamaları için kullandığı varsayılan seçeneklerle seri hale getirmek istiyorsanız yeni JsonSerializerOptions.Web singleton'ı kullanın.

string webJson = JsonSerializer.Serialize(
    new { SomeValue = 42 },
    JsonSerializerOptions.Web // Defaults to camelCase naming policy.
    );
Console.WriteLine(webJson);
// {"someValue":42}

JSON Şema Aktarıcısı

JSON, uzak yordam çağırma düzenlerinin bir parçası olarak yöntem imzalarındaki türleri göstermek için sıklıkla kullanılır. Örneğin, OpenAPI belirtimlerinin bir parçası olarak veya OpenAI gibi sağlayıcılardan alınan yapay zeka hizmetleriyle araç çağrısının bir parçası olarak kullanılır. Geliştiriciler System.Text.Json kullanarak .NET türlerini JSON olarak serileştirebilir ve seri durumdan çıkarabilir. Ancak.NET türünün şeklini açıklayan bir JSON şeması da alabilmeleri gerekir (yani, neyin seri hale getirileceği ve nelerin seri durumdan çıkarılabileceğini açıklar). System.Text.Json şimdi bir .NET türünü temsil eden bir JSON şeması oluşturmayı destekleyen türünü sağlar JsonSchemaExporter .

Daha fazla bilgi için bkz. JSON şema dışarı aktarma.

Nul atanabilir ek açıklamalara saygı gösterin

System.Text.Json artık özelliklere ait null atanabilirlik ek açıklamalarını tanır ve RespectNullableAnnotations bayrağı kullanılarak seri hale getirme ve seri durumdan çıkarma sırasında bunları zorunlu kılacak şekilde yapılandırılabilir.

Aşağıdaki kodda seçeneğin nasıl ayarlanacağı gösterilmektedir:

public static void RunIt()
{
    JsonSerializerOptions options = new() { RespectNullableAnnotations = true };

    // Throws exception: System.Text.Json.JsonException: The property or field
    // 'Title' on type 'Serialization+Book' doesn't allow getting null values.
    // Consider updating its nullability annotation.
    JsonSerializer.Serialize(new Book { Title = null! }, options);

    // Throws exception: System.Text.Json.JsonException: The property or field
    // 'Title' on type 'Serialization+Book' doesn't allow setting null values.
    // Consider updating its nullability annotation.
    JsonSerializer.Deserialize<Book>("""{ "Title" : null }""", options);
}

public class Book
{
    public required string Title { get; set; }
    public string? Author { get; set; }
    public int PublishYear { get; set; }
}

Daha fazla bilgi için Null değer atanabilir ek açıklamalarına saygı gösterin bölümüne bakın.

İsteğe bağlı olmayan oluşturucu parametreleri gerektir

Geçmişte, System.Text.Json oluşturucu tabanlı seri durumdan çıkarma kullanılırken isteğe bağlı olmayan oluşturucu parametrelerini isteğe bağlı olarak ele almıştır. Yeni RespectRequiredConstructorParameters bayrağını kullanarak bu davranışı değiştirebilirsiniz.

Aşağıdaki kodda seçeneğin nasıl ayarlanacağı gösterilmektedir:

JsonSerializerOptions options = new() { RespectRequiredConstructorParameters = true };

// Throws exception: System.Text.Json.JsonException: JSON deserialization
// for type 'Serialization+MyPoco' was missing required properties including: 'Value'.
JsonSerializer.Deserialize<MyPoco>("""{}""", options);

Türü MyPoco aşağıdaki gibi tanımlanır:

record MyPoco(string Value);

Daha fazla bilgi için bkz. İsteğe bağlı olmayan oluşturucu parametreleri.

JsonObject özelliklerini sıralama

Türü JsonObject şimdi özellik sıralamasının açıkça işlem görmesini sağlayan sıralı sözlük benzeri API'ler sunar.

JsonObject jObj = new()
{
    ["key1"] = true,
    ["key3"] = 3
};

Console.WriteLine(jObj is IList<KeyValuePair<string, JsonNode?>>); // True.

// Insert a new key-value pair at the correct position.
int key3Pos = jObj.IndexOf("key3") is int i and >= 0 ? i : 0;
jObj.Insert(key3Pos, "key2", "two");

foreach (KeyValuePair<string, JsonNode?> item in jObj)
{
    Console.WriteLine($"{item.Key}: {item.Value}");
}

// Output:
// key1: true
// key2: two
// key3: 3

Daha fazla bilgi için bkz . Özellik sırasını değiştirme.

Enum üye adlarını özelleştirin

Yeni System.Text.Json.Serialization.JsonStringEnumMemberNameAttribute öznitelik, dize olarak serileştirilen türlerin her bir enum üyesinin adını özelleştirmek için kullanılabilir.

JsonSerializer.Serialize(MyEnum.Value1 | MyEnum.Value2); // "Value1, Custom enum value"

[Flags, JsonConverter(typeof(JsonStringEnumConverter))]
enum MyEnum
{
    Value1 = 1,
    [JsonStringEnumMemberName("Custom enum value")]
    Value2 = 2,
}

Daha fazla bilgi için "Özel Sabit Listesi Üye Adları"nı bkz .

Birden çok JSON belgesini akışla aktarma

System.Text.Json.Utf8JsonReader artık tek bir arabellekten veya akıştan birden çok, boşlukla ayrılmış JSON belgesi okumayı destekliyor. Varsayılan olarak, okuyucu, ilk üst düzey belgeden sonra gelen boşluk olmayan karakterler algılarsa bir istisna atar. Bayrağını kullanarak AllowMultipleValues bu davranışı değiştirebilirsiniz.

Daha fazla bilgi için bkz. Birden çok JSON belgesini okuma.

Aralıklar

Yüksek performanslı kodda span'lar genellikle dizeleri gereksiz yere ayırmamak için kullanılır. Span<T> ve ReadOnlySpan<T>, .NET'te kod yazma yöntemlerini devrim yapmaya devam ediyor ve her sürümde uzantılar üzerinde çalışan daha fazla yöntem ekleniyor. .NET 9, span ile ilgili aşağıdaki güncelleştirmeleri içerir:

Dosya yardımcıları

Sınıf File, ReadOnlySpan<char>/ReadOnlySpan<byte> ve ReadOnlyMemory<char>/ReadOnlyMemory<byte>'yı kolayca ve doğrudan dosyalara yazmak için yeni yardımcılar kazandı.

Aşağıdaki kod, bir dosyaya ReadOnlySpan<char> verimli bir şekilde yazar.

ReadOnlySpan<char> text = ...;
File.WriteAllText(filePath, text);

Aralıklar için yeni StartsWith<T>(ReadOnlySpan<T>, T) ve EndsWith<T>(ReadOnlySpan<T>, T) uzantı yöntemleri de eklendi; böylece bir ReadOnlySpan<T>'nin belirli bir T değerle başlayıp başlamadığını veya bitip bitmediğini test etmek kolaylaşır.

Aşağıdaki kod bu yeni kolaylık API'lerini kullanır.

ReadOnlySpan<char> text = "some arbitrary text";
return text.StartsWith('"') && text.EndsWith('"'); // false

params ReadOnlySpan<T> Aşırı yüklemeler

C# her zaman dizi parametrelerinin params olarak işaretlenmesini desteklemiştir. Bu anahtar sözcük, basitleştirilmiş bir çağrı söz dizimi sağlar. Örneğin, yöntemin String.Join(String, String[]) ikinci parametresi ile paramsişaretlenir. Bu aşırı yüklemeyi bir diziyle veya değerleri tek tek geçirerek çağırabilirsiniz:

string result = string.Join(", ", new string[3] { "a", "b", "c" });
string result = string.Join(", ", "a", "b", "c");

.NET 9'un öncesinde, değerleri tek tek geçirdiğinizde, C# derleyicisi üç bağımsız değişken etrafında örtük bir dizi oluşturarak ilk çağrıyla aynı kodu yayar.

C# 13'ten itibaren params, aralıklar (Span<T> ve ReadOnlySpan<T>) dahil olmak üzere bir koleksiyon ifadesi aracılığıyla oluşturulabilen herhangi bir bağımsız değişkenle kullanabilirsiniz. Bu, kullanılabilirlik ve performans açısından faydalıdır. C# derleyicisi, bağımsız değişkenleri yığında depolayabilir, bunların etrafına bir "span" sarabilir ve bunu metoda aktarabilir, bu da aksi takdirde oluşacak olan örtük dizi ayırmasını önler.

.NET 9, parametresiyle params ReadOnlySpan<T> 60'ın üzerinde yöntem içerir. Bazıları yepyeni aşırı yüklemelerdir, bazıları ise daha önce bir ReadOnlySpan<T> alan ancak şimdi bu parametresi params ile işaretlenmiş olan mevcut yöntemlerdir. Net etkisi, .NET 9'a yükseltip kodunuzu yeniden derlerseniz herhangi bir kod değişikliği yapmadan performans geliştirmeleri görmenizdir. Bunun nedeni, derleyicinin dizi tabanlı aşırı yüklemelerden çok span tabanlı aşırı yüklemelere bağlanmayı tercih ediyor olmasıdır.

Örneğin, String.Join şimdi yeni deseni uygulayan aşağıdaki aşırı yüklemeyi içerir: String.Join(String, ReadOnlySpan<String>)

Şimdi, string.Join(", ", "a", "b", "c") gibi bir çağrı, "a", "b" ve "c" bağımsız değişkenlerini geçirmek için bir dizi ayırmadan yapılır.

ReadOnlySpan<karakteri> üzerinde numaralandır. Split() kesimleri

string.Split , sağlanan bir veya daha fazla ayırıcı ile bir dizeyi hızla bölümlendirmenin pratik bir yöntemidir. Ancak performansa odaklanan kodlar için string.Split'nin tahsis profili engelleyici olabilir, çünkü her ayrıştırılan bileşen için bir dize tahsis eder ve bunların tümünü depolamak için bir string[] kullanır. Ayrıca aralıklarla da çalışmaz, bu nedenle bir ReadOnlySpan<char> dizeniz varsa, üzerinde string.Split çağrısı yapabilmek için dizeye dönüştürdüğünüzde başka bir dize tahsis etmek zorunda kalırsınız.

.NET 8'de Split için bir SplitAny ve ReadOnlySpan<char> yöntemleri kümesi tanıtıldı. Bu yöntemler, yeni bir string[] döndürmek yerine, her bileşen için sınırlayıcı dizinlerin yazıldığı hedef Span<Range> kabul eder. Bu, işlemi tamamen hafıza tahsisi gerektirmeyen hale getirir. Bu yöntemlerin, aralık sayısı hem bilinen hem de küçük olduğunda kullanılması uygundur.

.NET 9'da, önceden bilinmeyen sayıda segmentle artımlı olarak bir Split'yi ayrıştırmaya izin vermek için yeni SplitAny ve ReadOnlySpan<T> aşırı yüklemeleri eklenmiştir. Yeni yöntemler, her bir segmentin numaralandırılmasına olanak tanır ve bu segment benzer şekilde özgün yayılma alanına dilimleme için kullanılabilecek bir Range olarak temsil edilir.

public static bool ListContainsItem(ReadOnlySpan<char> span, string item)
{
    foreach (Range segment in span.Split(','))
    {
        if (span[segment].SequenceEquals(item))
        {
            return true;
        }
    }

    return false;
}

Sistem.Biçimler

Bir TarEntry nesnesinin kapsayan akıştaki verilerinin konumu veya uzaklığı artık genel bir özelliktir. TarEntry.DataOffset girdinin arşiv akışında girdinin ilk veri baytının bulunduğu konumu döndürür. Girdinin verileri, aracılığıyla TarEntry.DataStreamerişebileceğiniz bir alt akışta kapsüllenmiş olur ve bu da verilerin arşiv akışına göre gerçek konumunu gizler. Bu çoğu kullanıcı için yeterlidir, ancak daha fazla esnekliğe ihtiyacınız varsa ve arşiv akışındaki verilerin gerçek başlangıç konumunu öğrenmek istiyorsanız, yeni TarEntry.DataOffset API çok büyük TAR dosyalarıyla eşzamanlı erişim gibi özellikleri desteklemeyi kolaylaştırır.

// Create stream for tar ball data in Azure Blob Storage.
BlobClient blobClient = new(connectionString, blobContainerName, blobName);
Stream blobClientStream = await blobClient.OpenReadAsync(options, cancellationToken);

// Create TarReader for the stream and get a TarEntry.
TarReader tarReader = new(blobClientStream);
System.Formats.Tar.TarEntry? tarEntry = await tarReader.GetNextEntryAsync();

if (tarEntry is null)
    return;

// Get position of TarEntry data in blob stream.
long entryOffsetInBlobStream = tarEntry.DataOffset;
long entryLength = tarEntry.Length;

// Create a separate stream.
Stream newBlobClientStream = await blobClient.OpenReadAsync(options, cancellationToken);
newBlobClientStream.Seek(entryOffsetInBlobStream, SeekOrigin.Begin);

// Read tar ball content from separate BlobClient stream.
byte[] bytes = new byte[entryLength];
await newBlobClientStream.ReadExactlyAsync(bytes, 0, (int)entryLength);

System.Guid

NewGuid()RFC 9562'deki UUID Sürüm 4 belirtimini izleyerek çoğunlukla Guid doldurulmuş bir oluşturur. Aynı RFC, "yaygın olarak uygulanan ve iyi bilinen Unix Dönem zaman damgası kaynağından türetilen zaman sıralamasına sahip bir değer alanı içeren" Sürüm 7 dahil olmak üzere diğer sürümleri de tanımlar. Başka bir deyişle, verilerin çoğu hala rastgeledir, ancak bazıları bu değerlerin doğal bir sıralama düzenine sahip olmasını sağlayan bir zaman damgasına göre veriler için ayrılmıştır. .NET 9'da, yeni Guid ve Guid.CreateVersion7() yöntemleri aracılığıyla Sürüm 7'ye göre bir Guid.CreateVersion7(DateTimeOffset) oluşturabilirsiniz. Yeni Version özelliğini kullanarak bir Guid nesnesinin sürüm alanını da alabilirsiniz.

System.IO

zlib-ng ile sıkıştırma

System.IO.Compression, , ZipArchiveve DeflateStream gibi GZipStreamZLibStreamözelliklerin tümü öncelikli olarak zlib kitaplığını temel alır. .NET 9'dan başlayarak, bu özelliklerin tümü daha geniş bir işletim sistemi ve donanım dizisinde daha tutarlı ve verimli işleme sağlayan zlib-ng kitaplığını kullanır.

ZLib ve Brotli sıkıştırma seçenekleri

ZLibCompressionOptionsve BrotliCompressionOptions algoritmaya özgü sıkıştırma düzeyini ve stratejisini (Default, Filtered, , HuffmanOnlyRunLengthEncodingveya Fixed) ayarlamak için yeni türlerdir. Bu türler, mevcut tek seçenek <olan System.IO.Compression.CompressionLevel'den daha hassas ayarlar isteyen kullanıcılara yöneliktir>.

Yeni sıkıştırma seçeneği türleri gelecekte genişletilebilir.

Aşağıdaki kod parçacığında bazı örnek kullanımlar gösterilmektedir:

private MemoryStream CompressStream(Stream uncompressedStream)
{
    MemoryStream compressorOutput = new();
    using ZLibStream compressionStream = new(
        compressorOutput,
        new ZLibCompressionOptions()
        {
            CompressionLevel = 6,
            CompressionStrategy = ZLibCompressionStrategy.HuffmanOnly
        }
        );
    uncompressedStream.CopyTo(compressionStream);
    compressionStream.Flush();

    return compressorOutput;
}

XPS sanal yazıcısından XPS belgeleri

Daha önce, bir V4 XPS sanal yazıcısından gelen XPS belgeleri System.IO.Packaging kitaplığını kullanılarak, .piece dosyalarını işleme desteği olmadığı için açılamıyordu. Bu boşluk .NET 9'da giderilmiştir.

Sistem.Sayılar

BigInteger üst sınırı

BigInteger temelde rastgele uzunluktaki tamsayı değerlerini temsil etme desteği sağlar. Ancak uygulamada uzunluk, kullanılabilir bellek veya belirli bir ifadeyi hesaplamanın ne kadar süreceği gibi temel bilgisayarın sınırlarıyla kısıtlanır. Ayrıca, çok büyük bir değere neden olan belirli girişlerde başarısız olan bazı API'ler vardır. Bu sınırlar nedeniyle, .NET 9 en fazla BigInteger uzunluğunu zorlar, yani en fazla (2^31) - 1 (yaklaşık 2,14 milyar) bit içerebilir. Böyle bir sayı neredeyse 256 MB ayırmayı temsil eder ve yaklaşık 646,5 milyon basamak içerir. Bu yeni sınır, açığa çıkarılan tüm API'lerin iyi bir şekilde çalışmasını ve tutarlılığını sağlarken, kullanım senaryolarının çoğunun çok ötesinde sayıların kullanılmasına olanak tanır.

BigMul API'ler

BigMul , iki sayının tam çarpımını üreten bir işlemdir. .NET 9, dönüş türü parametre türlerinden sonraki büyük BigMul olan , int, longve uint üzerinde ayrılmış ulong API'ler ekler.

Yeni API'ler şunlardır:

Vektör dönüştürme API'leri

.NET 9, Vector2, Vector3, Vector4, Quaternion ve Plane arasında dönüştürme için ayrılmış uzantı API'leri ekler.

Yeni API'ler aşağıdaki gibidir:

Aynı boyutlu dönüştürmeler için, Vector4, Quaternion ve Plane arasındaki dönüştürmeler sıfır maliyetlidir. Dönüştürmeleri daraltmak için de aynı şey söylenebilir; örneğin, Vector4'dan Vector2'e veya Vector3'e. Vector2 veya Vector3'den Vector4'ye genişletme dönüştürmeleri için, yeni öğeleri 0'a başlatan normal bir API ve bu yeni öğeleri tanımlanmamış bırakarak sıfır maliyetli olabilecek Unsafe sonekli bir API vardır.

Vektör oluşturma API'leri

Yeni Create API'ler, Vector, Vector2, Vector3 ve Vector4 için kullanıma sunulmuştur ve System.Runtime.Intrinsics ad alanında donanım vektör türleri için sağlanan eşdeğer API'lere paralellik gösterir.

Yeni API'ler hakkında daha fazla bilgi için bkz:

Bu API'ler, öncelikle .NET'in SIMD ile hızlandırılmış türleri genelinde kolaylık ve tutarlılığı sağlamak için kullanılır.

Ek hızlandırma

System.Numerics ad alanındaki BigInteger, Vector2, Vector3, Vector4, Quaternion ve Plane gibi birçok türde ek performans geliştirmeleri yapılmıştır.

Bazı durumlarda bu, çarpma, bir dizi köşeden Matrix4x4 oluşturma, Plane birleştirme ve çapraz çarpımını Quaternion hesaplama dahil olmak üzere çekirdek API'lerde 2-5 kat hız artışına neden oldu.

SinCos API'si için, her iki Sin(x) ve Cos(x)'yi tek bir çağrıda hesaplayarak daha verimli hale getiren sürekli katlama desteği de vardır.

Yapay zeka için tensorlar

Tensorlar, yapay zekanın (AI) köşe taşı veri yapısıdır. Bunlar genellikle çok boyutlu diziler olarak düşünülebilir.

Tensorlar şunlar için kullanılır:

  • Metin dizileri (belirteçler), görüntüler, video ve ses gibi verileri temsil eder ve kodlar.
  • Daha yüksek boyutlu verileri verimli bir şekilde işleme.
  • Hesaplamaları daha yüksek boyutlu verilere verimli bir şekilde uygulayın.
  • Ağırlık bilgilerini ve ara hesaplamaları (sinir ağlarında) depolayın.

.NET tensor API'lerini kullanmak için System.Numerics.Tensors NuGet paketini yükleyin.

Yeni Tensor<T> türü

Yeni Tensor<T> tür, .NET kitaplıklarının ve çalışma zamanının yapay zeka özelliklerini genişletir. Bu tür:

  • Mümkün olduğunda sıfır kopya kullanarak ML.NET, TorchSharp ve ONNX Runtime gibi yapay zeka kitaplıklarıyla verimli bir birlikte çalışma sağlar.
  • Verimli matematik işlemleri için TensorPrimitives üzerine kuruludur.
  • Dizin oluşturma ve dilimleme işlemleri sağlayarak kolay ve verimli veri işlemeye olanak tanır.
  • Mevcut yapay zeka ve makine öğrenmesi kitaplıklarının yerini almamaktadır. Bunun yerine, kod yinelemesini ve bağımlılıklarını azaltmak ve en son çalışma zamanı özelliklerini kullanarak daha iyi performans elde etmek için ortak bir API kümesi sağlamayı amaçlar.

Aşağıdaki kodlar, yeni Tensor<T> türe dahil edilen bazı API'leri gösterir.

// Create a tensor (1 x 3).
Tensor<int> t0 = Tensor.Create([1, 2, 3], [1, 3]); // [[1, 2, 3]]

// Reshape tensor (3 x 1).
Tensor<int> t1 = t0.Reshape(3, 1); // [[1], [2], [3]]

// Slice tensor (2 x 1).
Tensor<int> t2 = t1.Slice(1.., ..); // [[2], [3]]

// Broadcast tensor (3 x 1) -> (3 x 3).
// [
//  [ 1, 1, 1],
//  [ 2, 2, 2],
//  [ 3, 3, 3]
// ]
var t3 = Tensor.Broadcast<int>(t1, [3, 3]);

// Math operations.
var t4 = Tensor.Add(t0, 1); // [[2, 3, 4]]
var t5 = Tensor.Add(t0.AsReadOnlyTensorSpan(), t0); // [[2, 4, 6]]
var t6 = Tensor.Subtract(t0, 1); // [[0, 1, 2]]
var t7 = Tensor.Subtract(t0.AsReadOnlyTensorSpan(), t0); // [[0, 0, 0]]
var t8 = Tensor.Multiply(t0, 2); // [[2, 4, 6]]
var t9 = Tensor.Multiply(t0.AsReadOnlyTensorSpan(), t0); // [[1, 4, 9]]
var t10 = Tensor.Divide(t0, 2); // [[0.5, 1, 1.5]]
var t11 = Tensor.Divide(t0.AsReadOnlyTensorSpan(), t0); // [[1, 1, 1]]

Uyarı

Bu API, .NET 9 için deneysel olarak işaretlenir.

Tensör İlkelleri

Kütüphane System.Numerics.Tensors, değer aralıkları üzerinde sayısal işlemler gerçekleştirmek için statik yöntemler sağlayan TensorPrimitives sınıfını içerir. .NET 9'da, tarafından TensorPrimitives kullanıma sunulan yöntemlerin kapsamı önemli ölçüde genişletildi ve 40'tan (.NET 8'de) neredeyse 200 aşırı yüklemeye kadar büyüdü. Yüzey alanı Math ve MathF gibi türlerden tanıdık sayısal işlemleri kapsar. Ayrıca gibi INumber<TSelf>genel matematik arabirimlerini de içerir, ancak tek bir değeri işlemek yerine bir değer aralığını işlerler. .NET 9 için SIMD için iyileştirilmiş uygulamalar aracılığıyla da birçok işlem hızlandırılmıştır.

TensorPrimitives artık belirli bir arabirim uygulayan herhangi bir tür T için aşırı yüklemelerin genel versiyonlarını kullanıma sunar. (.NET 8 sürümü yalnızca float değerlerinin aralıklarını işlemek için aşırı yüklemeler içeriyor.) Örneğin, yeni CosineSimilarity<T>(ReadOnlySpan<T>, ReadOnlySpan<T>) aşırı yükleme, float, double veya Half değerlerinin iki vektörü ya da IRootFunctions<TSelf> uygulayan başka herhangi bir türün değerleri üzerinde kosinüs benzerliği hesaplar.

float ve double türündeki iki vektördeki kosinüs benzerlik işleminin duyarlılığını karşılaştırın.

ReadOnlySpan<float> vector1 = [1, 2, 3];
ReadOnlySpan<float> vector2 = [4, 5, 6];
Console.WriteLine(TensorPrimitives.CosineSimilarity(vector1, vector2));
// Prints 0.9746318

ReadOnlySpan<double> vector3 = [1, 2, 3];
ReadOnlySpan<double> vector4 = [4, 5, 6];
Console.WriteLine(TensorPrimitives.CosineSimilarity(vector3, vector4));
// Prints 0.9746318461970762

İplilik

İş parçacığı API'leri, görevler arasında yineleme için iyileştirmeler, önceliklendirilmiş kanallar için iyileştirmeler içerir. Bu kanallar, ilk giren ilk çıkar (FIFO) olmak yerine öğelerini sıralayabilir ve Interlocked.CompareExchange daha fazla tür için kullanılabilir.

Task.WhenEach

Nesnelerle Task<TResult> çalışmak için çeşitli yararlı yeni API'ler eklendi. Yeni Task.WhenEach yöntem, bir await foreach deyim kullanarak tamamlanan görevleri yinelemenize olanak tanır. Artık tamamlanan bir sonraki görevi seçmek için bir dizi göreve art arda Task.WaitAny çağrısı yapmanıza gerek yok.

Aşağıdaki kod birden çok HttpClient çağrı yapar ve tamamlandıklarında sonuçları üzerinde çalışır.

using HttpClient http = new();

Task<string> dotnet = http.GetStringAsync("http://dot.net");
Task<string> bing = http.GetStringAsync("http://www.bing.com");
Task<string> ms = http.GetStringAsync("http://microsoft.com");

await foreach (Task<string> t in Task.WhenEach(bing, dotnet, ms))
{
    Console.WriteLine(t.Result);
}

Önceliklendirilmiş ilişkisiz kanal

System.Threading.Channels ad alanı, CreateBounded ve CreateUnbounded yöntemlerini kullanarak ilk giren ilk çıkar (FIFO) kanalları oluşturmanıza olanak tanır. FIFO kanallarıyla, öğeler kanala yazıldıkları sırayla kanaldan okunur. .NET 9'da, CreateUnboundedPrioritized veya özel bir Comparer<T>.Default'ye göre en önemli kabul edilen öğe kanaldan okunan bir sonraki öğe olacak şekilde öğeleri sıralayan yeni IComparer<T> yöntemi eklenmiştir.

Aşağıdaki örnek, kanala farklı bir sırayla yazılmış olsalar bile 1 ile 5 arasında sayıları sırasıyla veren bir kanal oluşturmak için yeni yöntemi kullanır.

Channel<int> c = Channel.CreateUnboundedPrioritized<int>();

await c.Writer.WriteAsync(1);
await c.Writer.WriteAsync(5);
await c.Writer.WriteAsync(2);
await c.Writer.WriteAsync(4);
await c.Writer.WriteAsync(3);
c.Writer.Complete();

while (await c.Reader.WaitToReadAsync())
{
    while (c.Reader.TryRead(out int item))
    {
        Console.Write($"{item} ");
    }
}

// Output: 1 2 3 4 5

Daha fazla tür için Interlocked.CompareExchange

.NET'in önceki sürümlerinde, Interlocked.Exchange ve Interlocked.CompareExchange, int, uint, long, ulong, nint, nuint, float, double ve object ile çalışmak için aşırı yüklemelere sahipti ve herhangi bir başvuru türü T ile çalışmak için genel bir aşırı yükleme vardı. .NET 9'da byte, sbyte, short ve ushort ile atomik işlemler yapmak için yeni parametreler bulunuyor. Ayrıca, genel Interlocked.Exchange<T> ve Interlocked.CompareExchange<T> aşırı yüklemelerdeki kısıtlama kaldırıldı, bu nedenle bu yöntemler artık yalnızca başvuru türleri ile çalışmak zorunda değil. Artık, bahsedilen tüm türler ile bool ve char ve herhangi bir enum türünü içeren herhangi bir ilkel türle çalışabilirler.