.NET 8 런타임의 새로운 기능
이 문서에서는 .NET 8용 .NET 런타임의 새로운 기능에 대해 설명합니다.
성능 향상
.NET 8에는 코드 생성 및 JIT(Just-In-Time) 컴파일 기능이 향상되었습니다.
- Arm64 성능 향상
- SIMD 개선 사항
- AVX-512 ISA 확장 지원(Vector512 및 AVX-512참조)
- 클라우드 네이티브 개선 사항
- JIT 처리량 향상
- 루프 및 일반 최적화
- ThreadStaticAttribute 표시된 필드에 대한 액세스 최적화
- 연속 레지스터 할당 Arm64에는 테이블 벡터 조회를 위한 두 개의 명령어가 있으며, 이는 모든 항목이 튜플 피연산자에서 연속된 레지스터에 있어야 함을 필요로 합니다.
- 이제 JIT/NativeAOT는 컴파일 시간에 크기를 확인할 수 있는 경우, SIMD를 사용하여 비교, 복사 및 초기화를 포함한 일부 메모리 작업의 루프를 펼치고 자동 벡터화할 수 있습니다.
또한 동적 PGO(프로필 기반 최적화)가 개선되었으며 이제 기본적으로 사용하도록 설정됩니다. 더 이상 런타임 구성 옵션 사용하여 사용하도록 설정할 필요가 없습니다. 동적 PGO는 계층화된 컴파일과 함께 작동하여 계층 0 중에 배치되는 추가 계측에 따라 코드를 더욱 최적화합니다.
평균적으로 동적 PGO는 성능을 약 15%향상시킵니다. 약 4,600개의 테스트로 구성된 벤치마크 제품군에서, 23개의% 테스트에서 20% 이상의 성능 향상이 관찰되었습니다.
Codegen 구조체 승격
.NET 8에는 구조체 변수를 승격하는 JIT의 기능을 일반화하는 codegen에 대한 새로운 물리적 승격 최적화 패스가 포함되어 있습니다. 이 최적화(집계 스칼라 대체라고도 함)는 구조체 변수의 필드를 JIT가 추론하고 보다 정확하게 최적화할 수 있는 기본 변수로 대체합니다.
JIT는 이미 이 최적화를 지원했지만 다음을 비롯한 몇 가지 큰 제한 사항이 있습니다.
- 4개 이하의 필드가 있는 구조체에 대해서만 지원되었습니다.
- 각 필드가 기본 형식이거나 기본 형식을 래핑하는 간단한 구조체인 경우에만 지원되었습니다.
물리적 승격은 이러한 제한을 제거하여 여러 가지 오랜 JIT 문제를 해결합니다.
쓰레기 수거
.NET 8은 즉시 메모리 제한을 조정하는 기능을 추가합니다. 이는 수요가 오고 가는 클라우드 서비스 시나리오에서 유용합니다. 비용 효율성을 위해 서비스는 수요가 변동함에 따라 리소스 소비를 확장 및 축소해야 합니다. 서비스가 수요 감소를 감지하면 메모리 제한을 줄여 리소스 사용량을 축소할 수 있습니다. 이전에는 GC(가비지 수집기)가 변경 내용을 인식하지 못하고 새 제한보다 더 많은 메모리를 할당할 수 있으므로 실패했습니다. 이 변경으로 RefreshMemoryLimit() API를 호출하여 GC를 새 메모리 제한으로 업데이트할 수 있습니다.
주의해야 할 몇 가지 제한 사항은 다음과 같습니다.
- 32비트 플랫폼(예: Windows x86 및 Linux ARM)에서 .NET은 아직 없는 경우 새 힙 하드 제한을 설정할 수 없습니다.
- API는 새로 고침 실패를 나타내는 0이 아닌 상태 코드를 반환할 수 있습니다. 규모 축소가 너무 공격적이고 GC가 기동할 여지가 없는 경우에 발생할 수 있습니다. 이 경우
GC.Collect(2, GCCollectionMode.Aggressive)
호출하여 현재 메모리 사용량을 축소한 다음 다시 시도하는 것이 좋습니다. - GC가 시작 중에 프로세스가 처리할 수 있다고 생각하는 크기를 초과하여 메모리 제한을 확장하면
RefreshMemoryLimit
호출이 성공하지만 제한으로 인식되는 것보다 더 많은 메모리를 사용할 수는 없습니다.
다음 코드 조각은 API를 호출하는 방법을 보여줍니다.
GC.RefreshMemoryLimit();
메모리 제한과 관련된 일부 GC 구성 설정을 새로 고칠 수도 있습니다. 다음 코드 조각은 힙 하드 제한을 100 mebibytes(MiB)로 설정합니다.
AppContext.SetData("GCHeapHardLimit", (ulong)100 * 1_024 * 1_024);
GC.RefreshMemoryLimit();
API는 하드 제한이 유효하지 않을 때, 예를 들어 힙 하드 제한 백분율이 음수이거나 하드 제한이 너무 낮으면, InvalidOperationException 에러를 발생시킬 수 있습니다. 이는 새 AppData 설정으로 인해 또는 컨테이너 메모리 제한 변경으로 인해 새로 고침이 설정되는 힙 하드 제한이 이미 커밋된 것보다 낮은 경우에 발생할 수 있습니다.
모바일 앱의 세계화
iOS, tvOS 및 MacCatalyst의 모바일 앱은 더 가벼운 ICU 번들을 사용하는 새로운 하이브리드 세계화 모드를 선택할 수 있습니다. 하이브리드 모드에서 세계화 데이터는 ICU 번들에서 부분적으로 가져오고 부분적으로 네이티브 API 호출에서 가져옵니다. 하이브리드 모드는 모바일 이 지원하는 모든로캘을 제공합니다.
하이브리드 모드는 고정 세계화 모드에서 작동할 수 없고 모바일의 ICU 데이터에서 트리밍된 문화권을 사용하는 앱에 가장 적합합니다. 더 작은 ICU 데이터 파일을 로드하려는 경우에도 사용할 수 있습니다. (icudt_hybrid.dat 파일은 기본 ICU 데이터 파일 icudt.dat34.5 % 작습니다.)
하이브리드 세계화 모드를 사용하려면 HybridGlobalization
MSBuild 속성을 true로 설정합니다.
<PropertyGroup>
<HybridGlobalization>true</HybridGlobalization>
</PropertyGroup>
주의해야 할 몇 가지 제한 사항은 다음과 같습니다.
- 네이티브 API의 제한 사항으로 인해 하이브리드 모드에서 모든 세계화 API가 지원되지는 않습니다.
- 지원되는 API 중 일부는 다른 동작을 갖습니다.
애플리케이션이 영향을 받는지 확인하려면 동작 차이점참조하세요.
원본에서 생성된 COM interop
.NET 8에는 COM 인터페이스와의 상호 운용을 지원하는 새 원본 생성기가 포함되어 있습니다.
GeneratedComInterfaceAttribute 사용하여 인터페이스를 원본 생성기의 COM 인터페이스로 표시할 수 있습니다. 소스 생성기는 C# 코드에서 관리되지 않는 코드로 호출할 수 있도록 하는 코드를 생성합니다. 또한 비관리 코드에서 C#으로 호출할 수 있도록 하는 코드를 생성합니다. 이 소스 생성기는 LibraryImportAttribute과 통합되며, GeneratedComInterfaceAttribute이 있는 형식을 매개 변수 및 반환 형식으로 LibraryImport
특성이 지정된 메서드에서 사용할 수 있습니다.
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.Marshalling;
[GeneratedComInterface]
[Guid("5401c312-ab23-4dd3-aa40-3cb4b3a4683e")]
partial interface IComInterface
{
void DoWork();
}
internal partial class MyNativeLib
{
[LibraryImport(nameof(MyNativeLib))]
public static partial void GetComInterface(out IComInterface comInterface);
}
또한 소스 생성기는 GeneratedComInterfaceAttribute 특성을 사용하여 인터페이스를 구현하는 형식을 관리되지 않는 코드에 전달할 수 있도록 새 GeneratedComClassAttribute 특성을 지원합니다. 소스 생성기는 인터페이스를 구현하고 관리되는 구현에 호출을 전달하는 COM 개체를 노출하는 데 필요한 코드를 생성합니다.
GeneratedComInterfaceAttribute 특성이 있는 인터페이스의 메서드는 LibraryImportAttribute
동일한 형식을 모두 지원하며, LibraryImportAttribute
이제 GeneratedComInterface
-attributed 형식 및 GeneratedComClass
-attributed 형식을 지원합니다.
C# 코드는 GeneratedComInterface
특성 인터페이스만 사용하여 관리되지 않는 코드에서 COM 개체를 래핑하거나 C#에서 관리되는 개체를 래핑하여 관리되지 않는 코드에 노출하는 경우 Options 속성의 옵션을 사용하여 생성될 코드를 사용자 지정할 수 있습니다. 이러한 옵션은 사용되지 않을 것으로 알고 있는 시나리오에 대해 마샬러를 작성할 필요가 없음을 의미합니다.
소스 생성기는 새 StrategyBasedComWrappers 형식을 사용하여, COM 객체 래퍼 및 관리 개체 래퍼를 생성하고 관리합니다. 이 새로운 형식은 고급 사용자에 대한 사용자 지정 지점을 제공하면서 COM interop에 대한 예상 .NET 사용자 환경을 제공하는 것을 처리합니다. 애플리케이션에 COM에서 형식을 정의하는 고유한 메커니즘이 있거나 원본에서 생성된 COM이 현재 지원하지 않는 시나리오를 지원해야 하는 경우 새 StrategyBasedComWrappers 형식을 사용하여 시나리오에 대한 누락된 기능을 추가하고 COM 형식에 대해 동일한 .NET 사용자 환경을 가져오는 것이 좋습니다.
Visual Studio를 사용하는 경우 새 분석기 및 코드 수정을 통해 소스 생성 interop을 사용하도록 기존 COM interop 코드를 쉽게 변환할 수 있습니다.
ComImportAttribute이 있는 각 인터페이스 옆의 전구 아이콘은 소스 생성 인터럽으로 변환할 수 있는 옵션을 제공합니다. 수정은 GeneratedComInterfaceAttribute 특성을 사용하도록 인터페이스를 변경합니다. 또한 GeneratedComInterfaceAttribute
사용하여 인터페이스를 구현하는 모든 클래스 옆에 전구는 GeneratedComClassAttribute 특성을 형식에 추가하는 옵션을 제공합니다. 형식이 변환되면 DllImport
메서드를 이동하여 LibraryImportAttribute
사용할 수 있습니다.
제한
COM 소스 생성기는 아파트 친화성, new
키워드를 사용한 COM CoClass의 활성화 및 다음 API를 지원하지 않습니다.
- IDispatch기반 인터페이스입니다.
- IInspectable기반 인터페이스.
- COM 속성 및 이벤트입니다.
구성 바인딩 소스 생성기
.NET 8은 ASP.NET Core에서 AOT 및 트리밍 친화적인 구성 제공하는 원본 생성기를 도입했습니다. 생성기는 기존 리플렉션 기반 구현의 대안입니다.
원본 생성기는 Configure(TOptions), Bind및 Get 호출을 검색하여 형식 정보를 검색합니다. 프로젝트에서 생성기를 사용하도록 설정하면 컴파일러는 기존 리플렉션 기반 프레임워크 구현을 통해 생성된 메서드를 암시적으로 선택합니다.
생성기를 사용하기 위해 소스 코드 변경이 필요하지 않습니다. AOT로 컴파일된 웹 애플리케이션에서는 기본적으로 활성화되며, PublishTrimmed
이 true
로 설정된 경우 (.NET 8+ 앱)에도 활성화됩니다. 다른 프로젝트 형식의 경우 원본 생성기는 기본적으로 꺼져 있지만 프로젝트 파일에서 true
EnableConfigurationBindingGenerator
속성을 설정하여 옵트인할 수 있습니다.
<PropertyGroup>
<EnableConfigurationBindingGenerator>true</EnableConfigurationBindingGenerator>
</PropertyGroup>
다음 코드는 바인더를 호출하는 예제를 보여줍니다.
public class ConfigBindingSG
{
static void RunIt(params string[] args)
{
WebApplicationBuilder builder = WebApplication.CreateBuilder(args);
IConfigurationSection section = builder.Configuration.GetSection("MyOptions");
// !! Configure call - to be replaced with source-gen'd implementation
builder.Services.Configure<MyOptions>(section);
// !! Get call - to be replaced with source-gen'd implementation
MyOptions? options0 = section.Get<MyOptions>();
// !! Bind call - to be replaced with source-gen'd implementation
MyOptions options1 = new();
section.Bind(options1);
WebApplication app = builder.Build();
app.MapGet("/", () => "Hello World!");
app.Run();
}
public class MyOptions
{
public int A { get; set; }
public string S { get; set; }
public byte[] Data { get; set; }
public Dictionary<string, string> Values { get; set; }
public List<MyClass> Values2 { get; set; }
}
public class MyClass
{
public int SomethingElse { get; set; }
}
}
핵심 .NET 라이브러리
이 섹션에는 다음 하위 항목이 포함되어 있습니다.
- 리플렉션
- 직렬화
- 시간 추상화
- UTF8 개선 사항
- 임의성을 다루는 방법
- 성능 중심 유형
- System.Numerics 및 System.Runtime.Intrinsics
- 데이터 유효성 검사
- 지표
- 암호화
- 네트워킹
- 스트림 기반 ZipFile 메서드
반사
함수 포인터는 .NET 5에서 도입되었지만, 리플렉션에 대한 해당 지원은 당시에 추가되지 않았습니다. 함수 포인터에 typeof
또는 리플렉션을 사용하는 경우(예: typeof(delegate*<void>())
또는 FieldInfo.FieldType
각각) IntPtr 반환되었습니다. .NET 8부터 System.Type 개체가 대신 반환됩니다. 이 형식은 호출 규칙, 반환 형식 및 매개 변수를 포함하여 함수 포인터 메타데이터에 대한 액세스를 제공합니다.
메모
함수에 대한 물리적 주소인 함수 포인터 인스턴스는 계속해서 IntPtr표시됩니다. 리플렉션 형식만 변경되었습니다.
새 기능은 현재 CoreCLR 런타임 및 MetadataLoadContext에서만 구현되고 있습니다.
System.Type및 System.Reflection.PropertyInfo, System.Reflection.FieldInfo, System.Reflection.ParameterInfo에 IsFunctionPointer과 같은 새로운 API가 추가되었습니다. 다음 코드에서는 리플렉션에 새 API 중 일부를 사용하는 방법을 보여 줍니다.
using System;
using System.Reflection;
// Sample class that contains a function pointer field.
public unsafe class UClass
{
public delegate* unmanaged[Cdecl, SuppressGCTransition]<in int, void> _fp;
}
internal class FunctionPointerReflection
{
public static void RunIt()
{
FieldInfo? fieldInfo = typeof(UClass).GetField(nameof(UClass._fp));
// Obtain the function pointer type from a field.
Type? fpType = fieldInfo?.FieldType;
// New methods to determine if a type is a function pointer.
Console.WriteLine(
$"IsFunctionPointer: {fpType?.IsFunctionPointer}");
Console.WriteLine(
$"IsUnmanagedFunctionPointer: {fpType?.IsUnmanagedFunctionPointer}");
// New methods to obtain the return and parameter types.
Console.WriteLine($"Return type: {fpType?.GetFunctionPointerReturnType()}");
if (fpType is not null)
{
foreach (Type parameterType in fpType.GetFunctionPointerParameterTypes())
{
Console.WriteLine($"Parameter type: {parameterType}");
}
}
// Access to custom modifiers and calling conventions requires a "modified type".
Type? modifiedType = fieldInfo?.GetModifiedFieldType();
// A modified type forwards most members to its underlying type.
Type? normalType = modifiedType?.UnderlyingSystemType;
if (modifiedType is not null)
{
// New method to obtain the calling conventions.
foreach (Type callConv in modifiedType.GetFunctionPointerCallingConventions())
{
Console.WriteLine($"Calling convention: {callConv}");
}
}
// New method to obtain the custom modifiers.
Type[]? modifiers =
modifiedType?.GetFunctionPointerParameterTypes()[0].GetRequiredCustomModifiers();
if (modifiers is not null)
{
foreach (Type modreq in modifiers)
{
Console.WriteLine($"Required modifier for first parameter: {modreq}");
}
}
}
}
이전 예제에서는 다음 출력을 생성합니다.
IsFunctionPointer: True
IsUnmanagedFunctionPointer: True
Return type: System.Void
Parameter type: System.Int32&
Calling convention: System.Runtime.CompilerServices.CallConvSuppressGCTransition
Calling convention: System.Runtime.CompilerServices.CallConvCdecl
Required modifier for first parameter: System.Runtime.InteropServices.InAttribute
직렬화
.NET 8에서 System.Text.Json 직렬화 및 역직렬화 기능의 많은 개선이 이루어졌습니다. 예를 들어 POCO 에 없는 JSON 속성의 처리를 사용자 지정할수 있습니다.
다음 섹션에서는 다른 직렬화 개선 사항에 대해 설명합니다.
- 추가 형식에 대한 기본 지원
- 원본 생성기
- 인터페이스 계층 구조
- 명명 정책
- 읽기 전용 속성
- 리플렉션 기반 기본 사용 안 함
- 새 JsonNode API 메서드
- 비공용 멤버
- 스트리밍 역직렬화 API
- WithAddedModifier 확장 메서드
- 새 JsonContent. 오버로드 만들기
- JsonSerializerOptions 인스턴스를 불변으로 만들기
일반적으로 JSON 직렬화에 대한 자세한 내용은 .NET JSON 직렬화 및 역직렬화참조하세요.
추가 형식에 대한 내장 지원
serializer는 다음과 같은 추가 형식을 기본적으로 지원합니다.
Half, Int128및 UInt128 숫자 형식입니다.
Console.WriteLine(JsonSerializer.Serialize( [ Half.MaxValue, Int128.MaxValue, UInt128.MaxValue ] )); // [65500,170141183460469231731687303715884105727,340282366920938463463374607431768211455]
Memory<T> 및 ReadOnlyMemory<T> 값입니다.
byte
값은 Base64 문자열로 직렬화되고 다른 형식은 JSON 배열로 직렬화됩니다.JsonSerializer.Serialize<ReadOnlyMemory<byte>>(new byte[] { 1, 2, 3 }); // "AQID" JsonSerializer.Serialize<Memory<int>>(new int[] { 1, 2, 3 }); // [1,2,3]
원본 생성기
.NET 8에는 리플렉션 기반 직렬 변환기동등하게 Native AOT 환경을 만들기 위한 System.Text.Json 원본 생성기 향상된 기능이 포함되어 있습니다. 예를 들어:
원본 생성기는 이제
required
및init
속성을 사용하여 형식을 직렬화하도록 지원합니다. 모두 리플렉션 기반 serialization에서 이미 지원되었습니다.소스 생성 코드의 서식이 향상되었습니다.
JsonSourceGenerationOptionsAttribute와 JsonSerializerOptions의 기능 동등성. 자세한 내용은 옵션 지정(원본 생성)참조하세요.
추가 진단(예: SYSLIB1034 및 SYSLIB1039).
무시되거나 액세스할 수 없는 속성 형식은 포함하지 마세요.
임의 형식 종류 내에서
JsonSerializerContext
선언 중첩을 지원합니다.약한 형식의 소스 생성 시나리오에서 컴파일러 생성 형식 또는 표현할 수 없는 형식(,)을 지원합니다. 컴파일러가 생성하는 형식을 소스 생성기에서 명시적으로 지정할 수 없기 때문에, 이제 System.Text.Json는 런타임 시 가장 가까운 상위 유형을 확인하는 기능을 수행합니다. 이 결의안은 값을 직렬화할 가장 적절한 상위 형식을 결정합니다.
새 변환기 유형
JsonStringEnumConverter<TEnum>
. 기존 JsonStringEnumConverter 클래스는 Native AOT에서 지원되지 않습니다. 다음과 같이 열거형 형식에 주석을 달 수 있습니다.[JsonConverter(typeof(JsonStringEnumConverter<MyEnum>))] public enum MyEnum { Value1, Value2, Value3 } [JsonSerializable(typeof(MyEnum))] public partial class MyContext : JsonSerializerContext { }
새
JsonConverter.Type
속성을 사용하면 제네릭이 아닌JsonConverter
인스턴스의 형식을 조회할 수 있습니다.Dictionary<Type, JsonConverter> CreateDictionary(IEnumerable<JsonConverter> converters) => converters.Where(converter => converter.Type != null) .ToDictionary(converter => converter.Type!);
이 속성은
JsonConverterFactory
인스턴스에 대해null
을 반환하고JsonConverter<T>
인스턴스에 대해typeof(T)
를 반환하기 때문에 null 값을 허용합니다.
연쇄 소스 생성기
JsonSerializerOptions 클래스에는 기존 TypeInfoResolver 속성을 보완하는 새 TypeInfoResolverChain 속성이 포함되어 있습니다. 이러한 속성은 소스 생성기를 연결하기 위한 계약 사용자 지정에 사용됩니다. 새 속성을 추가하면 하나의 호출 사이트에서 모든 연결된 구성 요소를 지정할 필요가 없어지며, 나중에 추가할 수 있습니다. 또한 TypeInfoResolverChain 체인을 분석하거나 해당 체인에서 구성 요소를 제거할 수 있습니다. 자세한 내용은 소스 생성기결합을 참조하세요.
또한 JsonSerializerOptions.AddContext<TContext>() 이제 사용되지 않습니다. TypeInfoResolver 및 TypeInfoResolverChain 속성으로 대체되었습니다. 자세한 내용은 SYSLIB0049참조하세요.
인터페이스 계층 구조
.NET 8은 인터페이스 계층에서 속성을 직렬화하는 지원을 추가합니다.
다음 코드는 즉시 구현된 인터페이스와 해당 기본 인터페이스의 속성이 직렬화되는 예제를 보여줍니다.
public static void InterfaceHierarchies()
{
IDerived value = new DerivedImplement { Base = 0, Derived = 1 };
string json = JsonSerializer.Serialize(value);
Console.WriteLine(json); // {"Derived":1,"Base":0}
}
public interface IBase
{
public int Base { get; set; }
}
public interface IDerived : IBase
{
public int Derived { get; set; }
}
public class DerivedImplement : IDerived
{
public int Base { get; set; }
public int Derived { get; set; }
}
명명 정책
JsonNamingPolicy
snake_case
(밑줄 포함) 및 kebab-case
(하이픈 포함) 속성 이름 변환에 대한 새 명명 정책을 포함합니다. 기존 JsonNamingPolicy.CamelCase 정책과 유사하게 다음 정책을 사용합니다.
var options = new JsonSerializerOptions
{
PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower
};
JsonSerializer.Serialize(new { PropertyName = "value" }, options);
// { "property_name" : "value" }
자세한 내용은 기본 제공 명명 정책을 참조하세요.
읽기 전용 속성
이제 읽기 전용 필드 또는 속성(즉, set
접근자가 없는 필드)으로 역직렬화할 수 있습니다.
이 지원을 전역적으로 받으려면 새 옵션 PreferredObjectCreationHandling을(를) JsonObjectCreationHandling.Populate으로 설정해야 합니다. 호환성이 중요한 경우 속성을 채울 특정 형식 또는 개별 속성에 [JsonObjectCreationHandling(JsonObjectCreationHandling.Populate)]
특성을 배치하여 기능을 보다 세분화할 수도 있습니다.
예를 들어 두 개의 읽기 전용 속성이 있는 CustomerInfo
형식으로 역직렬화하는 다음 코드를 고려해 보세요.
public static void ReadOnlyProperties()
{
CustomerInfo customer = JsonSerializer.Deserialize<CustomerInfo>("""
{ "Names":["John Doe"], "Company":{"Name":"Contoso"} }
""")!;
Console.WriteLine(JsonSerializer.Serialize(customer));
}
class CompanyInfo
{
public required string Name { get; set; }
public string? PhoneNumber { get; set; }
}
[JsonObjectCreationHandling(JsonObjectCreationHandling.Populate)]
class CustomerInfo
{
// Both of these properties are read-only.
public List<string> Names { get; } = new();
public CompanyInfo Company { get; } = new()
{
Name = "N/A",
PhoneNumber = "N/A"
};
}
.NET 8 이전에는 입력 값이 무시되었고 Names
및 Company
속성이 기본값을 유지했습니다.
{"Names":[],"Company":{"Name":"N/A","PhoneNumber":"N/A"}}
이제 입력 값은 역직렬화하는 동안 읽기 전용 속성을 채우는 데 사용됩니다.
{"Names":["John Doe"],"Company":{"Name":"Contoso","PhoneNumber":"N/A"}}
채우기 역직렬화 동작에 대한 자세한 내용은 초기화된 속성 채우기를 참조하세요.
리플렉션 기반 기본값 사용 안 함
이제 기본적으로 리플렉션 기반 직렬 변환기를 사용하지 않도록 설정할 수 있습니다. 이 비활성화는 특히 트리밍 및 네이티브 AOT 앱에서 사용되지 않는 리플렉션 구성 요소의 우발적인 루팅을 방지하는 데 유용합니다.
JsonSerializerOptions 인수를 JsonSerializer serialization 및 deserialization 메서드에 전달하도록 요구하여 기본 리플렉션 기반 serialization을 사용하지 않도록 설정하려면 프로젝트 파일에서 JsonSerializerIsReflectionEnabledByDefault
MSBuild 속성을 false
설정합니다.
새 IsReflectionEnabledByDefault API를 사용하여 기능 스위치의 값을 확인합니다. System.Text.Json을 기반으로 라이브러리를 구축하는 작성자는 리플렉션 구성 요소를 실수로 고정하지 않고 기본 설정을 구성하는 속성을 활용할 수 있습니다.
자세한 내용은 리플렉션 기본값 사용 안 함참조하세요.
새 JsonNode API 메서드
JsonNode 및 System.Text.Json.Nodes.JsonArray 형식에는 다음과 같은 새 메서드가 포함됩니다.
public partial class JsonNode
{
// Creates a deep clone of the current node and all its descendants.
public JsonNode DeepClone();
// Returns true if the two nodes are equivalent JSON representations.
public static bool DeepEquals(JsonNode? node1, JsonNode? node2);
// Determines the JsonValueKind of the current node.
public JsonValueKind GetValueKind(JsonSerializerOptions options = null);
// If node is the value of a property in the parent
// object, returns its name.
// Throws InvalidOperationException otherwise.
public string GetPropertyName();
// If node is the element of a parent JsonArray,
// returns its index.
// Throws InvalidOperationException otherwise.
public int GetElementIndex();
// Replaces this instance with a new value,
// updating the parent object/array accordingly.
public void ReplaceWith<T>(T value);
// Asynchronously parses a stream as UTF-8 encoded data
// representing a single JSON value into a JsonNode.
public static Task<JsonNode?> ParseAsync(
Stream utf8Json,
JsonNodeOptions? nodeOptions = null,
JsonDocumentOptions documentOptions = default,
CancellationToken cancellationToken = default);
}
public partial class JsonArray
{
// Returns an IEnumerable<T> view of the current array.
public IEnumerable<T> GetValues<T>();
}
비공개 멤버
JsonIncludeAttribute 및 JsonConstructorAttribute 특성 주석을 사용하여 지정된 형식에 대한 serialization 계약에 public이 아닌 멤버를 옵트인할 수 있습니다.
public static void NonPublicMembers()
{
string json = JsonSerializer.Serialize(new MyPoco(42));
Console.WriteLine(json);
// {"X":42}
JsonSerializer.Deserialize<MyPoco>(json);
}
public class MyPoco
{
[JsonConstructor]
internal MyPoco(int x) => X = x;
[JsonInclude]
internal int X { get; }
}
자세한 내용은 변경할 수 없는 형식 및 public이 아닌 멤버 및 접근자사용을 참조하세요.
스트리밍 역직렬화 API
.NET 8에는 GetFromJsonAsAsyncEnumerable같은 새로운 IAsyncEnumerable<T> 스트리밍 역직렬화 확장 메서드가 포함되어 있습니다. Task<TResult>를 반환하는 유사한 메서드들이 있으며, 예를 들어 HttpClientJsonExtensions.GetFromJsonAsync이 있습니다. 새 확장 메서드는 스트리밍 API를 호출하고 IAsyncEnumerable<T>반환합니다.
다음 코드에서는 새 확장 메서드를 사용하는 방법을 보여 줍니다.
public async static void StreamingDeserialization()
{
const string RequestUri = "https://api.contoso.com/books";
using var client = new HttpClient();
IAsyncEnumerable<Book?> books = client.GetFromJsonAsAsyncEnumerable<Book>(RequestUri);
await foreach (Book? book in books)
{
Console.WriteLine($"Read book '{book?.title}'");
}
}
public record Book(int id, string title, string author, int publishedYear);
WithAddedModifier 확장 메서드
새 WithAddedModifier(IJsonTypeInfoResolver, Action<JsonTypeInfo>) 확장 메서드를 사용하면 임의 IJsonTypeInfoResolver
인스턴스의 serialization 계약을 쉽게 수정할 수 있습니다.
var options = new JsonSerializerOptions
{
TypeInfoResolver = MyContext.Default
.WithAddedModifier(static typeInfo =>
{
foreach (JsonPropertyInfo prop in typeInfo.Properties)
{
prop.Name = prop.Name.ToUpperInvariant();
}
})
};
새 JsonContent.오버로드 만들기
이제 트리밍 안전 계약 또는 소스 생성 계약을 사용해 JsonContent 인스턴스를 만들 수 있습니다. 새 메서드는 다음과 같습니다.
- JsonContent.Create(Object, JsonTypeInfo, MediaTypeHeaderValue)
- JsonContent.Create<T>(T, JsonTypeInfo<T>, MediaTypeHeaderValue)
var book = new Book(id: 42, "Title", "Author", publishedYear: 2023);
HttpContent content = JsonContent.Create(book, MyContext.Default.Book);
public record Book(int id, string title, string author, int publishedYear);
[JsonSerializable(typeof(Book))]
public partial class MyContext : JsonSerializerContext
{
}
JsonSerializerOptions 인스턴스를 변경 불가능하게 설정
다음 새 메서드를 사용하면 JsonSerializerOptions 인스턴스가 고정되는 시기를 제어할 수 있습니다.
JsonSerializerOptions.MakeReadOnly()
이 오버로드는 트리밍에 안전하도록 설계되었으므로, 옵션 인스턴스가 리졸버로 구성되지 않은 경우 예외를 발생시킵니다.
JsonSerializerOptions.MakeReadOnly(Boolean)
이 오버로드에
true
전달하면 옵션 인스턴스가 누락된 경우 기본 리플렉션 확인자를 사용하여 해당 인스턴스를 채웁니다. 이 메서드는RequiresUnreferenceCode
/RequiresDynamicCode
표시되므로 네이티브 AOT 애플리케이션에 적합하지 않습니다.
새 IsReadOnly 속성을 사용하면 옵션 인스턴스가 고정되었는지 확인할 수 있습니다.
시간 추상화
새 TimeProvider 클래스 및 ITimer 인터페이스는 테스트 시나리오에서 시간을 모의할 수 있는 시간 추상화 기능을 추가합니다. 또한 시간 추상화 기능을 사용하여 Task.Delay 및 Task.WaitAsync사용하여 시간 진행에 의존하는 Task 작업을 모의할 수 있습니다. 시간 추상화는 다음과 같은 필수 시간 작업을 지원합니다.
- 로컬 및 UTC 시간 검색
- 성능 측정을 위한 타임스탬프 가져오기
- 타이머 만들기
다음 코드 조각은 몇 가지 사용 예제를 보여 줍니다.
// Get system time.
DateTimeOffset utcNow = TimeProvider.System.GetUtcNow();
DateTimeOffset localNow = TimeProvider.System.GetLocalNow();
TimerCallback callback = s => ((State)s!).Signal();
// Create a timer using the time provider.
ITimer timer = _timeProvider.CreateTimer(
callback, null, TimeSpan.Zero, Timeout.InfiniteTimeSpan);
// Measure a period using the system time provider.
long providerTimestamp1 = TimeProvider.System.GetTimestamp();
long providerTimestamp2 = TimeProvider.System.GetTimestamp();
TimeSpan period = _timeProvider.GetElapsedTime(providerTimestamp1, providerTimestamp2);
// Create a time provider that works with a
// time zone that's different than the local time zone.
private class ZonedTimeProvider(TimeZoneInfo zoneInfo) : TimeProvider()
{
private readonly TimeZoneInfo _zoneInfo = zoneInfo ?? TimeZoneInfo.Local;
public override TimeZoneInfo LocalTimeZone => _zoneInfo;
public static TimeProvider FromLocalTimeZone(TimeZoneInfo zoneInfo) =>
new ZonedTimeProvider(zoneInfo);
}
UTF8 개선 사항
형식의 문자열과 유사한 표현을 대상 범위에 쓸 수 있도록 하려면 형식에 새 IUtf8SpanFormattable 인터페이스를 구현합니다. 이 새 인터페이스는 ISpanFormattable밀접하게 관련되어 있지만 UTF16 및 Span<char>
대신 UTF8 및 Span<byte>
대상으로 합니다.
모든 기본 형식(및 기타 형식)에서 IUtf8SpanFormattable이 구현되었으며, string
, Span<char>
, 또는 Span<byte>
을 대상으로 할 때도 동일한 공유 논리를 사용합니다. 모든 형식(새 "B" 이진 지정자포함) 및 모든 문화권에 대한 모든 지원을 제공합니다. 즉, 이제 Byte
, Complex
, Char
, DateOnly
, DateTime
, DateTimeOffset
, Decimal
, Double
, Guid
, Half
, IPAddress
, IPNetwork
, Int16
, Int32
, Int64
, Int128
, IntPtr
, NFloat
, SByte
, Single
, Rune
, TimeOnly
, TimeSpan
, UInt16
, UInt32
, UInt64
, UInt128
, UIntPtr
, Version
부터 UTF8로 직접 서식을 지정할 수 있습니다.
새로운 Utf8.TryWrite 메서드는 UTF16 기반의 기존 MemoryExtensions.TryWrite 메서드에 UTF8 기반 메서드를 제공합니다. 보간된 문자열 구문을 사용하여 복합 식의 형식을 UTF8 바이트 범위로 직접 지정할 수 있습니다. 예를 들면 다음과 같습니다.
static bool FormatHexVersion(
short major,
short minor,
short build,
short revision,
Span<byte> utf8Bytes,
out int bytesWritten) =>
Utf8.TryWrite(
utf8Bytes,
CultureInfo.InvariantCulture,
$"{major:X4}.{minor:X4}.{build:X4}.{revision:X4}",
out bytesWritten);
구현은 형식 값의 IUtf8SpanFormattable을 인식하고, 인식한 구현을 사용하여 UTF8 표현을 대상 스팬에 직접 씁니다.
또한 구현은 새 Encoding.TryGetBytes(ReadOnlySpan<Char>, Span<Byte>, Int32) 메서드를 활용하며, 이 메서드는 Encoding.TryGetChars(ReadOnlySpan<Byte>, Span<Char>, Int32) 해당 메서드와 함께 대상 범위로 인코딩 및 디코딩을 지원합니다. 범위가 결과 상태를 보유하기에 충분하지 않은 경우 메서드는 예외를 throw하는 대신 false
반환합니다.
임의성 작업 방법
System.Random 및 System.Security.Cryptography.RandomNumberGenerator 형식은 임의성 작업을 위한 두 가지 새로운 메서드를 도입합니다.
항목 가져오기<T>()
새 System.Random.GetItems 및 System.Security.Cryptography.RandomNumberGenerator.GetItems 메서드를 사용하면 입력 집합에서 지정된 수의 항목을 임의로 선택할 수 있습니다. 다음 예제에서는 System.Random.GetItems<T>()
(Random.Shared 속성에서 제공하는 인스턴스에서)를 사용하여 배열에 31개 항목을 임의로 삽입하는 방법을 보여 줍니다. 이 예제는 플레이어가 색이 지정된 단추 시퀀스를 기억해야 하는 "Simon" 게임에서 사용할 수 있습니다.
private static ReadOnlySpan<Button> s_allButtons = new[]
{
Button.Red,
Button.Green,
Button.Blue,
Button.Yellow,
};
// ...
Button[] thisRound = Random.Shared.GetItems(s_allButtons, 31);
// Rest of game goes here ...
순서 섞기<T>()
새 Random.Shuffle 및 RandomNumberGenerator.Shuffle<T>(Span<T>) 메서드를 사용하면 범위의 순서를 임의로 지정할 수 있습니다. 이러한 방법은 기계 학습에서 학습 편향을 줄이는 데 유용합니다(따라서 첫 번째 방법은 항상 학습이 아니며 마지막 방법은 항상 테스트하는 것이 아닙니다).
YourType[] trainingData = LoadTrainingData();
Random.Shared.Shuffle(trainingData);
IDataView sourceData = mlContext.Data.LoadFromEnumerable(trainingData);
DataOperationsCatalog.TrainTestData split = mlContext.Data.TrainTestSplit(sourceData);
model = chain.Fit(split.TrainSet);
IDataView predictions = model.Transform(split.TestSet);
// ...
성능 중심 유형
.NET 8에는 앱 성능 향상을 위한 몇 가지 새로운 형식이 도입되었습니다.
새 System.Collections.Frozen 네임스페이스에는 FrozenDictionary<TKey,TValue> 및 FrozenSet<T>컬렉션 형식이 포함됩니다. 이러한 형식은 컬렉션이 만들어지면 키와 값을 변경할 수 없습니다. 이 요구 사항을 통해 더 빠른 읽기 작업(예:
TryGetValue()
)을 수행할 수 있습니다. 이러한 형식은 처음 사용할 때 채워진 다음 수명이 긴 서비스 기간 동안 유지되는 컬렉션에 특히 유용합니다. 예를 들면 다음과 같습니다.private static readonly FrozenDictionary<string, bool> s_configurationData = LoadConfigurationData().ToFrozenDictionary(); // ... if (s_configurationData.TryGetValue(key, out bool setting) && setting) { Process(); }
MemoryExtensions.IndexOfAny 같은 메서드는 전달된 컬렉션 에서값이 처음으로 나타나는 위치를 찾습니다. 새 System.Buffers.SearchValues<T> 형식은 이러한 메서드에 전달되도록 설계되었습니다. 이에 따라 .NET 8은 새 형식의 인스턴스를 허용하는 MemoryExtensions.IndexOfAny 같은 메서드의 새 오버로드를 추가합니다. SearchValues<T>인스턴스를 만들 때 후속 검색을 최적화하는 데 필요한 모든 데이터는 파생됩니다. 즉, 작업이 미리 수행됩니다.
새 System.Text.CompositeFormat 형식은 컴파일 시간에 알려지지 않은 형식 문자열을 최적화하는 데 유용합니다(예: 리소스 파일에서 형식 문자열이 로드되는 경우). 문자열 구문 분석과 같은 작업을 수행하기 위해 약간의 추가 시간이 소요되지만 각 사용 시 작업이 수행되지 않도록 합니다.
private static readonly CompositeFormat s_rangeMessage = CompositeFormat.Parse(LoadRangeMessageResource()); // ... static string GetMessage(int min, int max) => string.Format(CultureInfo.InvariantCulture, s_rangeMessage, min, max);
새로운 System.IO.Hashing.XxHash3 및 System.IO.Hashing.XxHash128 형식은 빠른 XXH3 및 XXH128 해시 알고리즘의 구현을 제공합니다.
System.Numerics 및 System.Runtime.Intrinsics
이 섹션에서는 System.Numerics 및 System.Runtime.Intrinsics 네임스페이스의 향상된 기능에 대해 설명합니다.
-
Vector256<T>, Matrix3x2및 Matrix4x4 .NET 8에서 하드웨어 가속이 향상되었습니다. 예를 들어 Vector256<T> 가능한 경우 내부적으로
2x Vector128<T>
작업으로 다시 구현되었습니다. 이렇게 하면 arm64와 같이Vector128.IsHardwareAccelerated == true
있지만Vector256.IsHardwareAccelerated == false
경우 일부 함수의 부분 가속이 가능합니다. - 하드웨어 내장 함수에는 이제
ConstExpected
속성이 주석 처리됩니다. 이렇게 하면 기본 하드웨어에 상수가 필요할 때와 상수가 아닌 값이 예기치 않게 성능에 저하될 수 있는 경우를 사용자가 인식할 수 있습니다. -
Lerp(TSelf, TSelf, TSelf)
Lerp
API가 IFloatingPointIeee754<TSelf> 추가되었으므로float
(Single),double
(Double) 및 Half. 이 API를 사용하면 두 값 간의 선형 보간을 효율적이고 올바르게 수행할 수 있습니다.
Vector512 및 AVX-512
.NET Core 3.0은 x86/x64용 플랫폼별 하드웨어 내장 API를 포함하도록 SIMD 지원을 확장했습니다. .NET 5는 Arm64 및 .NET 7에 대한 지원을 추가하여 플랫폼 간 하드웨어 내장 함수를 추가했습니다. .NET 8은 Intel Advanced Vector Extensions 512(AVX-512) 지침에 대한 Vector512<T> 및 지원을 도입하여 SIMD 지원을 강화합니다.
특히 .NET 8에는 AVX-512의 다음 주요 기능에 대한 지원이 포함되어 있습니다.
- 512비트 벡터 작업
- 추가 16개의 SIMD 레지스터
- 128비트, 256비트 및 512비트 벡터에 사용할 수 있는 추가 지침
이제 기능을 지원하는 하드웨어가 있는 경우, Vector512.IsHardwareAccelerated이(가) true
을(를) 보고합니다.
.NET 8은 또한 System.Runtime.Intrinsics.X86 네임스페이스 아래에 여러 플랫폼별 클래스를 추가합니다.
- Avx512F(기본)
- Avx512BW(바이트 및 단어)
- Avx512CD(충돌 탐지)
- Avx512DQ(더블워드 및 쿼드워드)
- Avx512Vbmi(벡터 바이트 조작 명령)
이러한 클래스는 64비트 프로세스에서만 사용할 수 있는 명령에 대해 IsSupported 속성 및 중첩된 Avx512F.X64 클래스를 노출한다는 측면에서 ISA(명령 집합 아키텍처)와 동일한 일반 셰이프를 따릅니다. 또한 각 클래스에는 해당 명령 집합에 대한 Avx512VL
(벡터 길이) 확장을 노출하는 중첩된 Avx512F.VL 클래스가 있습니다.
코드에서 Vector512
특정 또는 Avx512F
특정 지침을 명시적으로 사용하지 않더라도 새 AVX-512 지원을 계속 활용할 수 있습니다. JIT는 Vector128<T> 또는 Vector256<T>사용할 때 암시적으로 추가 레지스터 및 지침을 활용할 수 있습니다. 기본 클래스 라이브러리는 Span<T> 및 ReadOnlySpan<T> 의해 노출되는 대부분의 작업과 기본 형식에 대해 노출된 많은 수학 API에서 내부적으로 이러한 하드웨어 내장 함수를 사용합니다.
데이터 유효성 검사
System.ComponentModel.DataAnnotations 네임스페이스에는 클라우드 네이티브 서비스의 유효성 검사 시나리오를 위한 새 데이터 유효성 검사 특성이 포함되어 있습니다. 기존 DataAnnotations
유효성 검사기는 양식의 필드와 같은 일반적인 UI 데이터 항목 유효성 검사에 맞춰지지만 새 특성은 구성 옵션과 같은 비 사용자 항목 데이터의 유효성을 검사하도록 설계되었습니다. 새 특성 외에도 새 속성이 RangeAttribute 형식에 추가되었습니다.
새 API | 묘사 |
---|---|
RangeAttribute.MinimumIsExclusive RangeAttribute.MaximumIsExclusive |
경계가 허용 범위에 포함되는지 여부를 지정합니다. |
System.ComponentModel.DataAnnotations.LengthAttribute | 문자열 또는 컬렉션의 하한과 상한을 모두 지정합니다. 예를 들어 [Length(10, 20)] 컬렉션에 10개 이상의 요소와 최대 20개의 요소가 필요합니다. |
System.ComponentModel.DataAnnotations.Base64StringAttribute | 문자열이 유효한 Base64 표현인지 확인합니다. |
System.ComponentModel.DataAnnotations.AllowedValuesAttribute System.ComponentModel.DataAnnotations.DeniedValuesAttribute |
허용 목록과 거부 목록을 각각 지정합니다. 예를 들어 [AllowedValues("apple", "banana", "mango")] . |
지표
새 API를 사용하면 개체를 만들 때 Meter 및 Instrument 개체에 키-값 쌍 태그를 연결할 수 있습니다. 게시된 메트릭 측정값의 집계자는 태그를 사용하여 집계된 값을 구분할 수 있습니다.
var options = new MeterOptions("name")
{
Version = "version",
// Attach these tags to the created meter.
Tags = new TagList()
{
{ "MeterKey1", "MeterValue1" },
{ "MeterKey2", "MeterValue2" }
}
};
Meter meter = meterFactory!.Create(options);
Counter<int> counterInstrument = meter.CreateCounter<int>(
"counter", null, null, new TagList() { { "counterKey1", "counterValue1" } }
);
counterInstrument.Add(1);
새 API에는 다음이 포함됩니다.
- MeterOptions
- Meter(MeterOptions)
- CreateCounter<T>(String, String, String, IEnumerable<KeyValuePair<String,Object>>)
암호화
.NET 8은 SHA-3 해시 기본 형식에 대한 지원을 추가합니다. (SHA-3은 현재 OpenSSL 1.1.1 이상 및 Windows 11 빌드 25324 이상을 사용하는 Linux에서 지원됩니다.) SHA-2를 사용할 수 있는 API는 이제 SHA-3 칭찬을 제공합니다. 여기에는 해시에 대한 SHA3_256
, SHA3_384
및 SHA3_512
포함됩니다. HMAC에 대한 HMACSHA3_256
, HMACSHA3_384
및 HMACSHA3_512
. 알고리즘을 구성할 수 있는 해시에 대한 HashAlgorithmName.SHA3_256
, HashAlgorithmName.SHA3_384
및 HashAlgorithmName.SHA3_512
. RSA OAEP 암호화에 대한 RSAEncryptionPadding.OaepSHA3_256
, RSAEncryptionPadding.OaepSHA3_384
및 RSAEncryptionPadding.OaepSHA3_512
.
다음 예제에서는 플랫폼이 SHA-3을 지원하는지 여부를 확인하기 위해 SHA3_256.IsSupported
속성을 포함하여 API를 사용하는 방법을 보여 줍니다.
// Hashing example
if (SHA3_256.IsSupported)
{
byte[] hash = SHA3_256.HashData(dataToHash);
}
else
{
// ...
}
// Signing example
if (SHA3_256.IsSupported)
{
using ECDsa ec = ECDsa.Create(ECCurve.NamedCurves.nistP256);
byte[] signature = ec.SignData(dataToBeSigned, HashAlgorithmName.SHA3_256);
}
else
{
// ...
}
SHA-3 지원은 현재 암호화 기본 형식을 지원하기 위한 것입니다. 상위 수준 생성 및 프로토콜은 처음에 SHA-3을 완전히 지원하지 않을 것으로 예상됩니다. 이러한 프로토콜에는 X.509 인증서, SignedXml및 COSE가 포함됩니다.
네트워킹
HTTPS 프록시 지원
지금까지 HttpClient 지원되는 프록시 형식은 모두 "man-in-the-middle"을 허용하여 HTTPS URI에 대해서도 클라이언트가 연결 중인 사이트를 확인할 수 있도록 했습니다. 이제 HttpClient는 클라이언트와 프록시 간에 암호화된 채널을 생성하여 모든 요청을 완전한 개인정보 보호 상태에서 처리할 수 있는 HTTPS 프록시를 지원합니다.
HTTPS 프록시를 사용하도록 설정하려면 all_proxy
환경 변수를 설정하거나 WebProxy 클래스를 사용하여 프로그래밍 방식으로 프록시를 제어합니다.
Unix: export all_proxy=https://x.x.x.x:3218
Windows: set all_proxy=https://x.x.x.x:3218
WebProxy 클래스를 사용하여 프로그래밍 방식으로 프록시를 제어할 수도 있습니다.
스트림 기반 ZipFile 메서드
.NET 8에는 디렉터리에 포함된 모든 파일을 수집하고 압축한 다음 결과 zip 파일을 제공된 스트림에 저장할 수 있는 새로운 ZipFile.CreateFromDirectory 오버로드가 포함되어 있습니다. 마찬가지로 새 ZipFile.ExtractToDirectory 오버로드를 사용하면 압축된 파일이 포함된 스트림을 제공하고 파일 시스템에 해당 콘텐츠를 추출할 수 있습니다. 새 오버로드는 다음과 같습니다.
namespace System.IO.Compression;
public static partial class ZipFile
{
public static void CreateFromDirectory(
string sourceDirectoryName, Stream destination);
public static void CreateFromDirectory(
string sourceDirectoryName,
Stream destination,
CompressionLevel compressionLevel,
bool includeBaseDirectory);
public static void CreateFromDirectory(
string sourceDirectoryName,
Stream destination,
CompressionLevel compressionLevel,
bool includeBaseDirectory,
Encoding? entryNameEncoding);
public static void ExtractToDirectory(
Stream source, string destinationDirectoryName) { }
public static void ExtractToDirectory(
Stream source, string destinationDirectoryName, bool overwriteFiles) { }
public static void ExtractToDirectory(
Stream source, string destinationDirectoryName, Encoding? entryNameEncoding) { }
public static void ExtractToDirectory(
Stream source, string destinationDirectoryName, Encoding? entryNameEncoding, bool overwriteFiles) { }
}
이러한 새 API는 디스크 공간을 제한할 때 디스크를 중간 단계로 사용할 필요가 없으므로 유용할 수 있습니다.
확장 라이브러리
이 섹션에는 다음 하위 항목이 포함되어 있습니다.
- 옵션 유효성 검사
- LoggerMessageAttribute 생성자
- 확장 메트릭
- 호스팅된 수명 주기 서비스
- 키드 DI 서비스
- System.Numerics.Tensors.TensorPrimitives
키 지정된 DI 서비스
DI(키 종속성 주입) 서비스는 키를 사용하여 DI 서비스를 등록하고 검색하는 방법을 제공합니다. 키를 사용하여 서비스를 등록하고 사용하는 방법의 범위를 지정할 수 있습니다. 다음은 몇 가지 새로운 API입니다.
- IKeyedServiceProvider 인터페이스입니다.
- 생성자의 등록/확인에 사용된 키를 삽입하는 데 사용할 수 있는 ServiceKeyAttribute 특성입니다.
- 서비스 생성자 매개 변수에서 사용할 키 지정 서비스를 지정하는 데 사용할 수 있는 FromKeyedServicesAttribute 특성입니다.
- 키 지정된 서비스를 지원하기 위한 IServiceCollection 위한 다양한 새 확장 메서드(예: ServiceCollectionServiceExtensions.AddKeyedScoped.)
- IKeyedServiceProvider의 ServiceProvider 구현입니다.
다음 예제에서는 키 지정된 DI 서비스를 사용하는 방법을 보여 줍니다.
WebApplicationBuilder builder = WebApplication.CreateBuilder(args);
builder.Services.AddSingleton<BigCacheConsumer>();
builder.Services.AddSingleton<SmallCacheConsumer>();
builder.Services.AddKeyedSingleton<ICache, BigCache>("big");
builder.Services.AddKeyedSingleton<ICache, SmallCache>("small");
WebApplication app = builder.Build();
app.MapGet("/big", (BigCacheConsumer data) => data.GetData());
app.MapGet("/small", (SmallCacheConsumer data) => data.GetData());
app.MapGet("/big-cache", ([FromKeyedServices("big")] ICache cache) => cache.Get("data"));
app.MapGet("/small-cache", (HttpContext httpContext) => httpContext.RequestServices.GetRequiredKeyedService<ICache>("small").Get("data"));
app.Run();
class BigCacheConsumer([FromKeyedServices("big")] ICache cache)
{
public object? GetData() => cache.Get("data");
}
class SmallCacheConsumer(IServiceProvider serviceProvider)
{
public object? GetData() => serviceProvider.GetRequiredKeyedService<ICache>("small").Get("data");
}
public interface ICache
{
object Get(string key);
}
public class BigCache : ICache
{
public object Get(string key) => $"Resolving {key} from big cache.";
}
public class SmallCache : ICache
{
public object Get(string key) => $"Resolving {key} from small cache.";
}
자세한 내용은 dotnet/runtime#64427참조하세요.
호스트된 수명 주기 서비스
이제 호스트된 서비스에는 애플리케이션 수명 주기 동안 더 많은 실행 옵션이 있습니다.
IHostedService
StartAsync
및 StopAsync
제공되었으며, 이제 IHostedLifecycleService 다음과 같은 추가 메서드를 제공합니다.
- StartingAsync(CancellationToken)
- StartedAsync(CancellationToken)
- StoppingAsync(CancellationToken)
- StoppedAsync(CancellationToken)
이러한 메서드는 각각 기존 지점 전후에 실행됩니다.
다음 예제에서는 새 API를 사용하는 방법을 보여 줍니다.
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
internal class HostedLifecycleServices
{
public async static void RunIt()
{
IHostBuilder hostBuilder = new HostBuilder();
hostBuilder.ConfigureServices(services =>
{
services.AddHostedService<MyService>();
});
using (IHost host = hostBuilder.Build())
{
await host.StartAsync();
}
}
public class MyService : IHostedLifecycleService
{
public Task StartingAsync(CancellationToken cancellationToken) => /* add logic here */ Task.CompletedTask;
public Task StartAsync(CancellationToken cancellationToken) => /* add logic here */ Task.CompletedTask;
public Task StartedAsync(CancellationToken cancellationToken) => /* add logic here */ Task.CompletedTask;
public Task StopAsync(CancellationToken cancellationToken) => /* add logic here */ Task.CompletedTask;
public Task StoppedAsync(CancellationToken cancellationToken) => /* add logic here */ Task.CompletedTask;
public Task StoppingAsync(CancellationToken cancellationToken) => /* add logic here */ Task.CompletedTask;
}
}
자세한 내용은 dotnet/runtime#86511참조하세요.
옵션 유효성 검사
원본 생성기
시작 오버헤드를 줄이고 유효성 검사 기능 집합을 개선하기 위해 유효성 검사 논리를 구현하는 소스 코드 생성기를 도입했습니다. 다음 코드에서는 예제 모델 및 유효성 검사기 클래스를 보여 줍니다.
public class FirstModelNoNamespace
{
[Required]
[MinLength(5)]
public string P1 { get; set; } = string.Empty;
[Microsoft.Extensions.Options.ValidateObjectMembers(
typeof(SecondValidatorNoNamespace))]
public SecondModelNoNamespace? P2 { get; set; }
}
public class SecondModelNoNamespace
{
[Required]
[MinLength(5)]
public string P4 { get; set; } = string.Empty;
}
[OptionsValidator]
public partial class FirstValidatorNoNamespace
: IValidateOptions<FirstModelNoNamespace>
{
}
[OptionsValidator]
public partial class SecondValidatorNoNamespace
: IValidateOptions<SecondModelNoNamespace>
{
}
앱에서 종속성 주입을 사용하는 경우 다음 예제 코드와 같이 유효성 검사를 삽입할 수 있습니다.
WebApplicationBuilder builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllersWithViews();
builder.Services.Configure<FirstModelNoNamespace>(
builder.Configuration.GetSection("some string"));
builder.Services.AddSingleton<
IValidateOptions<FirstModelNoNamespace>, FirstValidatorNoNamespace>();
builder.Services.AddSingleton<
IValidateOptions<SecondModelNoNamespace>, SecondValidatorNoNamespace>();
ValidateOptionsResultBuilder 형식
.NET 8에서는 ValidateOptionsResult 개체를 쉽게 만들 수 있도록 ValidateOptionsResultBuilder 형식을 도입했습니다. 중요한 것은 이 빌더가 여러 오류의 누적을 가능하게 한다는 점입니다. 이전에는 IValidateOptions<TOptions>.Validate(String, TOptions) 구현하는 데 필요한 ValidateOptionsResult 개체를 만드는 것이 어려웠으며 때로는 계층화된 유효성 검사 오류가 발생했습니다. 여러 오류가 있는 경우 첫 번째 오류에서 유효성 검사 프로세스가 중지되는 경우가 많습니다.
다음 코드 조각은 ValidateOptionsResultBuilder사용하는 예제를 보여 줍니다.
ValidateOptionsResultBuilder builder = new();
builder.AddError("Error: invalid operation code");
builder.AddResult(ValidateOptionsResult.Fail("Invalid request parameters"));
builder.AddError("Malformed link", "Url");
// Build ValidateOptionsResult object has accumulating multiple errors.
ValidateOptionsResult result = builder.Build();
// Reset the builder to allow using it in new validation operation.
builder.Clear();
LoggerMessageAttribute 생성자
이제 LoggerMessageAttribute 추가 생성자 오버로드를 제공합니다. 이전에는 매개 변수가 없는 생성자 또는 모든 매개 변수(이벤트 ID, 로그 수준 및 메시지)가 필요한 생성자를 선택해야 했습니다. 새 오버로드는 코드가 감소된 필수 매개 변수를 지정하는 데 더 큰 유연성을 제공합니다. 이벤트 ID를 제공하지 않으면 시스템에서 자동으로 생성됩니다.
public LoggerMessageAttribute(LogLevel level, string message);
public LoggerMessageAttribute(LogLevel level);
public LoggerMessageAttribute(string message);
확장 지표
IMeterFactory 인터페이스
DI(종속성 주입) 컨테이너에 새 IMeterFactory 인터페이스를 등록하고 이를 사용하여 격리된 방식으로 Meter 개체를 만들 수 있습니다.
기본 미터 팩터리 구현을 사용하여 DI 컨테이너에 IMeterFactory 등록합니다.
// 'services' is the DI IServiceCollection.
services.AddMetrics();
그런 다음 소비자는 미터 팩터리를 가져와서 새 Meter 개체를 만드는 데 사용할 수 있습니다.
IMeterFactory meterFactory = serviceProvider.GetRequiredService<IMeterFactory>();
MeterOptions options = new MeterOptions("MeterName")
{
Version = "version",
};
Meter meter = meterFactory.Create(options);
MetricCollector<T> 클래스
새 MetricCollector<T> 클래스를 사용하면 타임스탬프와 함께 메트릭 측정값을 기록할 수 있습니다. 또한 클래스는 정확한 타임스탬프 생성을 위해 선택한 시간 공급자를 유연하게 사용할 수 있습니다.
const string CounterName = "MyCounter";
DateTimeOffset now = DateTimeOffset.Now;
var timeProvider = new FakeTimeProvider(now);
using var meter = new Meter(Guid.NewGuid().ToString());
Counter<long> counter = meter.CreateCounter<long>(CounterName);
using var collector = new MetricCollector<long>(counter, timeProvider);
Assert.IsNull(collector.LastMeasurement);
counter.Add(3);
// Verify the update was recorded.
Assert.AreEqual(counter, collector.Instrument);
Assert.IsNotNull(collector.LastMeasurement);
Assert.AreSame(collector.GetMeasurementSnapshot().Last(), collector.LastMeasurement);
Assert.AreEqual(3, collector.LastMeasurement.Value);
Assert.AreEqual(now, collector.LastMeasurement.Timestamp);
System.Numerics.Tensors.TensorPrimitives
업데이트된 System.Numerics.Tensors NuGet 패키지에는 텐서 작업에 대한 지원을 추가하는 새 System.Numerics.Tensors.TensorPrimitives 형식의 API가 포함되어 있습니다. 텐서 기본 형식은 AI 및 기계 학습과 같은 데이터 집약적 워크로드를 최적화합니다.
의미 체계 검색 및 RAG(검색 보강 세대)와 같은 AI 워크로드는 관련 데이터로 프롬프트를 보강하여 ChatGPT와 같은 대규모 언어 모델의 자연어 기능을 확장합니다. 이러한 워크로드의 경우 질문에 대답할 가장 관련성이 큰 데이터를 찾기 위한 코사인 유사성 같은 벡터에 대한 작업이 매우 중요합니다. TensorPrimitives 형식은 벡터 작업에 대한 API를 제공합니다.
자세한 내용은 .NET 8 RC 2 출시를 알리는 블로그 게시물을 참조하세요.
기본 AOT 지원
네이티브 AOT으로 게시하는 옵션은 .NET 7에서 처음 도입되었습니다. Native AOT를 사용하여 앱을 게시하면 런타임이 필요하지 않은 완전히 자체 포함된 버전의 앱이 만들어집니다. 모든 것이 단일 파일에 포함됩니다. .NET 8은 네이티브 AOT 게시에 다음과 같은 개선 사항을 제공합니다.
macOSx64 및 Arm64 아키텍처에 대한 지원을 추가합니다.
Linux에서 네이티브 AOT 앱의 크기를 최대 50%줄입니다. 다음 표에서는 .NET 7과 .NET 8의 전체 .NET 런타임을 포함하는 네이티브 AOT를 사용하여 게시된 "Hello World" 앱의 크기를 보여 니다.
운영 체제 .NET 7 .NET 8 Linux x64( -p:StripSymbols=true
포함)3.76MB 1.84MB Windows x64 2.85MB 1.77MB 최적화 기본 설정(크기 또는 속도)을 지정할 수 있습니다. 기본적으로 컴파일러는 애플리케이션의 크기를 염두에 두고 빠른 코드를 생성하도록 선택합니다. 그러나
<OptimizationPreference>
MSBuild 속성을 사용하여 하나 또는 다른 속성에 대해 특별히 최적화할 수 있습니다. 자세한 내용은 AOT 배포 최적화를 참조하세요.
네이티브 AOT를 사용하여 iOS와 유사한 플랫폼 대상 지정
.NET 8은 iOS와 유사한 플랫폼에 대해 네이티브 AOT 지원을 사용하도록 설정하는 작업을 시작합니다. 이제 다음 플랫폼에서 Native AOT를 사용하여 .NET iOS 및 .NET MAUI 애플리케이션을 빌드하고 실행할 수 있습니다.
ios
iossimulator
maccatalyst
tvos
tvossimulator
예비 테스트에 따르면 모노 대신 네이티브 AOT를 사용하는 .NET iOS 앱의 경우 디스크의 앱 크기가 약 35% 감소합니다. .NET MAUI iOS 앱용 디스크의 앱 크기는 최대 50%감소합니다. 또한 시작 시간도 더 빠릅니다. .NET iOS 앱은 시작 시간이 약%% 더 빠르고, .NET MAUI iOS 앱은 Mono에 비해 시작 성능이 약%% 더 우수합니다. .NET 8 지원은 실험적이며 기능 전체의 첫 번째 단계일 뿐입니다. 보다 자세한 내용은 .NET MAUI 블로그 게시물의 .NET 8 성능 향상을 참조하세요.
네이티브 AOT 지원은 앱 배포를 위한 옵트인 기능으로 사용할 수 있습니다. Mono는 여전히 앱 개발 및 배포의 기본 런타임입니다. iOS 디바이스에서 Native AOT를 사용하여 .NET MAUI 애플리케이션을 빌드하고 실행하려면 dotnet workload install maui
사용하여 .NET MAUI 워크로드를 설치하고 dotnet new maui -n HelloMaui
앱을 만듭니다. 그런 다음 프로젝트 파일에서 MSBuild 속성 PublishAot
을 true
으로 설정합니다.
<PropertyGroup>
<PublishAot>true</PublishAot>
</PropertyGroup>
다음 예제와 같이 필수 속성을 설정하고 dotnet publish
실행하면 네이티브 AOT를 사용하여 앱이 배포됩니다.
dotnet publish -f net8.0-ios -c Release -r ios-arm64 /t:Run
제한
모든 iOS 기능이 네이티브 AOT와 호환되는 것은 아닙니다. 마찬가지로 iOS에서 일반적으로 사용되는 모든 라이브러리가 NativeAOT와 호환되는 것은 아닙니다. 또한 네이티브 AOT 배포 기존제한 사항 외에도 다음 목록에서는 iOS와 유사한 플랫폼을 대상으로 지정할 때 몇 가지 다른 제한 사항을 보여 줍니다.
- 네이티브 AOT 사용은 앱 배포(
dotnet publish
) 중에만 사용하도록 설정됩니다. - 관리 코드 디버깅은 Mono에서만 지원됩니다.
- .NET MAUI 프레임워크와의 호환성이 제한됩니다.
Android 앱용 AOT 컴파일
앱 크기를 줄이기 위해 Android를 대상으로 하는 .NET 및 .NET MAUI 앱은 릴리스 모드에서 빌드될 때 프로파일된 AOT(미리) 컴파일 모드를 사용합니다. 프로파일된 AOT 컴파일은 일반 AOT 컴파일보다 더 적은 수의 메서드에 영향을 줍니다. .NET 8에는 Android 앱에 대한 AOT 컴파일을 추가로 옵트인하여 앱 크기를 훨씬 더 줄일 수 있는 <AndroidStripILAfterAOT>
속성이 도입되었습니다.
<PropertyGroup>
<AndroidStripILAfterAOT>true</AndroidStripILAfterAOT>
</PropertyGroup>
기본적으로 AndroidStripILAfterAOT
에서 true
로 설정하면 기본 AndroidEnableProfiledAot
설정이 재정의되어 AOT 컴파일된 거의 모든 메서드를 트리밍할 수 있습니다. 두 속성을 모두 true
로 명시적으로 설정하여 프로파일된 AOT와 IL 스트리핑을 함께 사용할 수도 있습니다.
<PropertyGroup>
<AndroidStripILAfterAOT>true</AndroidStripILAfterAOT>
<AndroidEnableProfiledAot>true</AndroidEnableProfiledAot>
</PropertyGroup>
교차 빌드된 Windows 앱
Windows가 아닌 플랫폼에서 Windows를 대상으로 하는 앱을 빌드할 때 결과 실행 파일은 이제 지정된 Win32 리소스(예: 애플리케이션 아이콘, 매니페스트, 버전 정보)로 업데이트됩니다.
이전에는 이러한 리소스를 갖기 위해 Windows에서 애플리케이션을 빌드해야 했습니다. 인프라 복잡성과 리소스 사용량 모두에 영향을 미치는 중요한 문제이기 때문에 크로스 빌딩 지원에서 이러한 격차를 해결하는 것이 인기 있는 요청이었습니다.
참고 항목
.NET