.NET 6에 포함된 신규 BCL API
다음과 같은 좋은 소개 글이 있군요. ^^
A preview of all of the new runtime features added in .NET 6
; https://threadreaderapp.com/thread/1422816504060416002.html
현재(2021-08-10)
.NET 6 preview 6이 나온 상태이고, 위의 글에 소개된 기능들은 모두 테스트가 가능합니다. 그럼, 이참에 같이 따라 해 볼까요? ^^
우선, FileStream을 사용하지 않고 C++처럼 저수준의 Handle만으로 파일 연산을 할 수 있는 API가 추가되었습니다.
static void lowLevelFileOperation()
{
using var handle = File.OpenHandle("ConsoleApp1.exe");
var length = RandomAccess.GetLength(handle);
Console.WriteLine($"FileLength: {length}");
}
그다음, "Process" 개체 생성 없이 현재 프로세스에 한해 경로와 id를 접근할 수 있는 속성이 Environment에 추가되었습니다.
static void getProcessInfoWihtoutProcessObject()
{
var pid = Environment.ProcessId;
var path = Environment.ProcessPath;
Console.WriteLine($"ProcessId: {pid}");
Console.WriteLine($"ProcessPath: {path}");
}
GC를 최소화하기 위한 눈물 겨운 노력으로 보입니다. ^^
CSPNG(Cryptographically Secure Pseudorandom Number Generator)의 사용법도 아주 단순한 유형으로 하나 제공합니다. 가령, 4바이트 무작위 정수를 반환받고 싶다면 이렇게 호출할 수 있습니다.
static void produceRandomInt4()
{
var bytes = RandomNumberGenerator.GetBytes(4);
Console.WriteLine($"Random Int4: {BitConverter.ToInt32(bytes)}");
}
Parallel.ForEachAsync도 추가되었습니다.
static async Task parallelDownload()
{
var urlsToDownload = new[]
{
"https://www.bing.com",
"https://www.google.com",
"https://search.yahoo.com/"
};
var client = new HttpClient();
// https://stackoverflow.com/questions/146134/how-to-remove-illegal-characters-from-path-and-filenames
string invalidChars = new string(Path.GetInvalidFileNameChars()) + new string(Path.GetInvalidPathChars());
var path = Path.GetDirectoryName(Environment.ProcessPath);
await Parallel.ForEachAsync(urlsToDownload, async (url, token) =>
{
Regex r = new Regex(string.Format("[{0}]", Regex.Escape(invalidChars)));
var targetPath = Path.Combine(path, r.Replace(url, "_") + ".txt");
var response = await client.GetAsync(url);
if (response.IsSuccessStatusCode)
{
using var target = File.OpenWrite(targetPath);
await response.Content.CopyToAsync(target);
Console.WriteLine($"{targetPath}: downloaded");
}
});
}
IEnumerable<T> LINQ 확장 메서드에도 역시 도우미 메서드들이 많이 추가되었다고 합니다. 아래는 컬렉션의 개수를 특정 묶음으로 나누는 Chunk 메서드이고,
static void ImprovedLinqMethods()
{
int chunkNumber = 1;
foreach (int[] chunk in Enumerable.Range(0, 9).Chunk(4))
{
Console.WriteLine($"Chunk {chunkNumber++}");
foreach (var item in chunk)
{
Console.WriteLine(item);
}
}
}
/* 출력 결과
Chunk 1
0
1
2
3
Chunk 2
4
5
6
7
Chunk 3
8
*/
새롭게 MaxBy, MinBy가 기존의 IEnumerable.Max/Min을 보완해 추가되었습니다.
var people = new People[]
{
new People{ Name = "tester", Age = 30 },
new People{ Name = "admin", Age = 25 },
};
Console.WriteLine(people.Max((elem) => elem.Age)); // 출력 결과: 30
Console.WriteLine(people.Max(new AgeComparer())); // 출력 결과: People { Name = tester, Age = 30 }
Console.WriteLine(people.MaxBy((elem) => elem.Age)); // 출력 결과: People { Name = tester, Age = 30 }
Console.WriteLine(people.MinBy((elem) => elem.Age)); // 출력 결과: People { Name = tester, Age = 30 }
보는 바와 같이, 기본형이 아닌 경우 Max/Min은 해당 인스턴스를 반환받고 싶다면 IComparer를 구현한 개체를 반환해야 했지만, MaxBy/MinBy는 비교할 멤버를 지정하면서도 개체를 결과로 반환해 주고 있습니다.
BitOperations에 추가된
IsPow2,
RoundUpToPowerOf2 메서드는 그냥 추가되었다는 것만 기억해도 좋을 듯싶고,
static void testBitOperation()
{
uint bufferSize = 235;
if (!BitOperations.IsPow2(bufferSize))
{
bufferSize = BitOperations.RoundUpToPowerOf2(bufferSize);
}
Console.WriteLine(bufferSize); // 출력 결과 256
}
비동기 메서드 호출 시, 해당 작업이 특정 시간 내에 끝나지 않으면 예외를 발생시키도록 만드는 것은 기억해 둘 만하겠습니다.
static async Task WaitOnly10Sec()
{
var task = Task.Run(() =>
{
return Task.Delay(5 * 1000);
});
// await task; // 원래는 5초를 대기해야 하지만,
Console.WriteLine($"[start] Task.Run: {DateTime.Now}");
await task.WaitAsync(TimeSpan.FromSeconds(2));
Console.WriteLine($"[end] Task.Run: {DateTime.Now}");
}
/* 실행 결과: 2초가 지나 예외 발생
[start] Task.Run: 2021-08-08 오후 10:54:05
Unhandled exception. System.TimeoutException: The operation has timed out.
at ConsoleApp1.Program.WaitOnly10Sec()
at ConsoleApp1.Program.Main(String[] args)
at ConsoleApp1.Program.(String[] args) in ConsoleApp1.dll:token 0x600000e+0xc
*/
ASP.NET Core의 경우
Configuration.GetRequiredSection 메서드가 추가되어 필수 설정이 없는 경우 예외를 발생시키는 것을 지원합니다.
public IConfiguration Configuration { get; }
class MyOptions
{
public string? SettingValue { get; set; }
}
public void ConfigureServices(IServiceCollection services)
{
var options = new MyOptions();
// 추가된 appsettings.json에 "MyOptions"가 없는 경우 예외 발생
// System.InvalidOperationException: 'Section 'MyOptions' not found in configuration.'
Configuration.GetRequiredSection("MyOptions").Bind(options);
services.AddControllers();
}
따라서, 이 오류로 인해 개발자는 명시적으로 해당 속성을 appsettings.json에 추가해야 한다는 것을 인지할 수 있습니다.
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"AllowedHosts": "*",
"MyOptions": {
"SettingValue": "test_var_1",
}
}
이와 함께,
@OpenTelemetry 지원을 위한 API를 내장하게 되었습니다. 가령 아래의 예제는 ASP.NET Core Web API 프로젝트에 추가했기 때문에 Web API의 호출 수를 제공하게 됩니다.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseRouting();
app.UseAuthorization();
var meter = new Meter("Microsoft.AspnetCore", "v1.0");
Counter<int> counter = meter.CreateCounter<int>("Requests");
app.Use((context, next) =>
{
counter.Add(1, KeyValuePair.Create<string, object?>("path", context.Request.Path.ToString()));
return next(context);
});
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
(
첨부 파일은 이 글의 예제 코드를 포함합니다.)
참고로,
원 글에서는 이 외에도 Preview 7에 포함된 3가지 기능도 함께 소개하고 있습니다.
Make improvements to signal handling on .NET applications
; https://github.com/dotnet/runtime/issues/50527
Implement NativeMemory #54006
; https://github.com/dotnet/runtime/pull/54006
Add ArgumentNullException.ThrowIfNull
; https://github.com/dotnet/runtime/pull/55594
기타!
Using the new PriorityQueue from .NET 6 (Preview 2)
; https://blog.elmah.io/using-the-new-priorityqueue-from-net-6/
[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]