First preview version of .NET 10.0 extends the class libraries
The first preview version of the tenth .NET version contains a number of innovations in the class libraries as well as new language features in C# 14.0.
(Image: Pincasso/Shutterstock.com)
- Dr. Holger Schwichtenberg
Two months after the release of .NET 9.0, Microsoft is starting the preview series for .NET 10.0. The first preview version is available in the .NET download area at Microsoft (see Figure 1).
This version will not be installed with the Visual Studio 2022 updates that are released at the same time (see Figure 2).
(Image:Â Screenshot (Holger Schwichtenberg))
(Image:Â Screenshot (Holger Schwichtenberg))
Compile applications with .NET 10.0 Preview 1, but in both Visual Studio 2022 17.13.1 and Visual Studio 2022 17.14.0 Preview 1.1. However, .NET 10.0 is not available for selection when creating a project in Visual Studio 2022 17.13.1. Here you have to set <TargetFramework>net10.0</TargetFramework> manually in the project. Visual Studio 2022 17.14.0 Preview 1.1 already knows .NET 10.0.
LINQ operators LeftJoin() and RightJoin()
As in the last two .NET versions, Microsoft is again providing new operators for Language Integrated Query (LINQ). This time, LeftJoin() and RightJoin() are two elementary operators from set theory and relational databases. In fact, these operations were already possible in LINQ, but with the help of a grouping and DefaultIfEmpty(). The new methods LeftJoin() and RightJoin() simplify their use:
CUI.H2("--- LeftJoin ALT seit .NET Framework 3.5 ---");
var AllCompaniesWithWebsitesSetOld = outer
.GroupJoin(inner,
c => c.ID,
w => w.CompanyID,
(c, websites) => new { Company = c, Websites = websites })
.SelectMany(
x => x.Websites.DefaultIfEmpty(), // Falls keine Webseite existiert, wird `null` verwendet
(c, w) => new WebsiteWithCompany
{
Name = c.Company.Name,
URL = w.URL, // Falls `w` null ist, bleibt URL null
City = c.Company.City
});
foreach (var item in AllCompaniesWithWebsitesSetOld)
{
Console.WriteLine((item.Name != null ? item.Name + " " + item.City :
"- keine Firma - ") + " -> " + (item.URL ?? "- keine URL -"));
}
CUI.H2("--- LeftJoin NEU ab .NET 10.0 ---");
var AllCompaniesWithWebsitesSet = outer.LeftJoin(inner, e => e.ID, e => e.CompanyID, (c, w)
=> new WebsiteWithCompany { Name = c.Name, City = c.City, URL = w.URL });
foreach (var item in AllCompaniesWithWebsitesSet)
{
Console.WriteLine((item.Name != null ? item.Name + " " + item.City :
"- keine Firma - ") + " -> " + (item.URL ?? "- keine URL -"));
}
CUI.H2("--- RightJoin OLD seit .NET Framework 3.5 ---");
var WebsiteWithCompanySetOLD = inner
.GroupJoin(outer,
w => w.CompanyID,
c => c.ID,
(w, companies) => new { Website = w, Companies = companies })
.SelectMany(
x => x.Companies.DefaultIfEmpty(), // Falls kein Unternehmen existiert, bleibt `null`
(w, c) => new WebsiteWithCompany
{
Name = c.Name, // Falls `c` null ist, bleibt `Name` null
City = c.City, // Falls `c` null ist, bleibt `City` null
URL = w.Website.URL
});
foreach (var item in WebsiteWithCompanySetOLD)
{
Console.WriteLine((item.Name != null ? item.Name + " " + item.City :
"- keine Firma - ") + " -> " + (item.URL ?? "- keine URL -"));
}
CUI.H2("--- RightJoin NEU ab .NET 10.0 ---");
var WebsiteWithCompanySet = outer.RightJoin(inner, e => e.ID, e => e.CompanyID, (c, w)
=> new WebsiteWithCompany { Name = c.Name, City = c.City, URL = w.URL });
foreach (var item in WebsiteWithCompanySet)
{
Console.WriteLine((item.Name != null ? item.Name + " " + item.City :
"- keine Firma - ") + " -> " + (item.URL ?? "- keine URL -"));
}
// Zum Vergleich: Inner Join, den es seit .NET Framework 3.5 gibt
CUI.H2("--- InnerJoin seit .NET Framework 3.5 ---");
var CompaniesWithWebsitesSet = outer.Join(inner,
c => c.ID,
w => w.CompanyID,
(c, w) => new WebsiteWithCompany
{
Name = c.Name,
URL = w.URL,
City = c.City
});
foreach (var item in CompaniesWithWebsitesSet)
{
Console.WriteLine((item.Name != null ? item.Name + " " + item.City :
"- keine Firma - ") + " -> " + (item.URL ?? "- keine URL -"));
}
The new operators LeftJoin() and RightJoin() are also supported in Entity Framework Core 10.0 for database accesses, see the issues “Support the new .NET 10 LeftJoin operators” and”Fully support right outer joins”.
TryAdd() with index return
The new generic class System.Collections.Generic.OrderedDictionary<T,T> introduced in .NET 9.0 already offered a method TryAdd(), which attempts to add an element. In addition to the existing variant TryAdd(TKey key, TValue value), there is now also TryAdd(TKey key, TValue value, out int index). This new overload returns the index if the element already exists in the set. Similarly, TryGetValue() is now available with an overload that not only returns the value of an entry, but also the position by index:
OrderedDictionary<string, string> websites = new OrderedDictionary<string, string>();
websites.Add("Heise", "www.Heise.de");
websites.Add("Microsoft", "www.Microsoft.com");
websites.Add("IT-Visions", "www.IT-Visions.de");
var propertyName = "IT-Visions";
var value = "www.IT-Visions.de";
// bisher
if (!websites.TryAdd(propertyName, value))
{
int index1 = websites.IndexOf(propertyName); // Second lookup operation
CUI.Warning("Element " + value + " ist bereits vorhanden!");
}
// neu
if (!websites.TryAdd(propertyName, value, out int index))
{
CUI.Warning("Element " + value +
" ist bereits vorhanden an der Position " +
index + """!""");
}
// neu
if (websites.TryGetValue(propertyName, out string? value2, out int index2))
{
CUI.Success($"Element {value2} wurde gefunden an der Position {index2}.");
}
Check IP addresses
To check IP addresses, there is now the static method IPAddress.TryParse(), which retrieves an IP address from a character string or
the types ReadOnlySpan<char> or ReadOnlySpan<byte>. The return value in bool and the extracted IP address is delivered in the form of an instance of the IPAddress class as a out parameter. If you only want to check whether the IP address is correct, write IPAddress.TryParse(input, out _).
In .NET 10.0, Microsoft offers another variant with less internal effort in the static method IsValid(), for example System.Net.IPAddress.IsValid("192.168.1.0"). Microsoft now simply turns a previously internal method TargetHostNameHelper.IsValidAddress() outwards.
GetPropertyCount() in System.Text.Json.JsonElement
In the JSON library, Microsoft has extended the JsonElement class with the GetPropertyCount() method, which can be used to determine how many properties a JSON object has:
JsonElement element1 = JsonSerializer.Deserialize<JsonElement>("""{ "ID" : 1, "Name" : "Dr. Holger Schwichtenberg", "Website": "www.IT-Visions.de" }""");
Console.WriteLine(element1.GetPropertyCount()); // 3
Previously, it was only possible to determine the number of objects in an array:
JsonElement element2 = JsonSerializer.Deserialize<JsonElement>("""[ 1, 2, 3, 4 ]""");
Console.WriteLine(element2.GetArrayLength()); // 4
In the JsonArray class in the System.Text.Json.Nodes namespace, Microsoft offers the two new methods RemoveRange() and RemoveAll(). RemoveRange() accepts two numbers for index and count. The index is zero-based as usual: array.RemoveRange(5, 2) removes the sixth and seventh elements. RemoveAll() expects a Predicate<JsonNode> object and allows the specification of a deletion criterion. For example array.RemoveAll(n => n.GetValue<int>() > 5):
System.Text.Json.Nodes.JsonArray array = JsonSerializer.Deserialize<System.Text.Json.Nodes.JsonArray>("""[ 1, 2, 3, 4, 5, 6, 7, 8, 9 ]""");
System.Console.WriteLine(array.Count); // 9
PrintJsonArray(array);
array.RemoveRange(5, 2);
PrintJsonArray(array);
array.RemoveAll(n => n.GetValue<int>() > 5);
PrintJsonArray(array);
Further improvements in the class library
Microsoft has also implemented the following in .NET 10.0 Preview 1:
- In the class
X509Certificate2Collection, the new methodFindByThumbprint()exists to find a certificate based on the SHA fingerprint - The
ISOWeekclass offers three new methods for handling theDateOnlydata type. Previously, onlySystem.DateTimewas possible here: - The implementation of the processing of ZIP archives has been optimized. Among other things, according to Microsoft, the updating of ZIP archives has been accelerated by 99.8Â % (adding a 2 GB file from 177.7 ms to 0.13 ms) and now uses 99.99Â % less RAM (now 7.01 KB instead of 2 GB RAM).
- The annotation
[JsonSourceGenerationOptions]for the source generator in the libraryText.Jsonnow also allows setting the behavior for circular references toUnspecified,PreserveorIgnoreCycles - In Windows Forms, some overloads of the
GetData()method for the clipboard and drag-and-drop operations that still use theBinaryFormatter, which Microsoft has expanded in .NET 9.0 and for which a separate NuGet package is now required, are now marked as “obsolete”. Instead, there are now new operations that work with JSON, includingSetDataAsJson()andTryGetData().
According to the release notes, there are only quality improvementsin WPF. There are new settings in .NET MAUI 10.0. In addition, Android projects can now be started directly from the command line with dotnet run. In the .NET SDK there is an optimization to speed up the package restore.