https://learn.microsoft.com/ko-kr/dotnet/core/whats-new/dotnet-8

 

.NET 8의 새로운 기능

.NET 8에 도입된 새로운 .NET 기능에 대해 알아봅니다.

learn.microsoft.com

.NET 8의 새로운 기능

  • 아티클
  • 2023. 11. 14.
  •  

이 문서의 내용

  1. .NET 갈망
  2. ASP.NET Core
  3. 핵심 .NET 라이브러리
  4. 확장 라이브러리

.NET 8은 .NET 7후속 작업입니다. 3년 동안 LTS(장기 지원) 릴리스로 지원됩니다. 여기에서 .NET 8을 다운로드할 수 있습니다.

.NET 갈망

.NET Aspire는 관찰 가능한 프로덕션 준비 분산 애플리케이션을 빌드하기 위한 의견 있는 클라우드 준비 스택입니다. .NET Aspire는 특정 클라우드 네이티브 문제를 처리하는 NuGet 패키지 컬렉션을 통해 제공되며 .NET 8용 미리 보기로 제공됩니다. 자세한 내용은 .NET Aspire(미리 보기)를 참조 하세요.

ASP.NET Core

ASP.NET Core 의 새로운 기능과 관련하여 ASP.NET Core 8.0의 새로운 기능

핵심 .NET 라이브러리

이 섹션에는 다음 하위 항목이 포함되어 있습니다.

직렬화

.NET 8에서 직렬화 및 역직렬화 기능이 많이 개선되었습니다 System.Text.Json . 예를 들어 JSON 페이로드에 없는 멤버의 처리를 사용자 지정할 수 있습니다.

다음 섹션에서는 다른 직렬화 개선 사항에 대해 설명합니다.

일반적으로 JSON serialization에 대한 자세한 내용은 .NET의 JSON serialization 및 deserialization을 참조하세요.

추가 형식에 대한 기본 제공 지원

serializer는 다음과 같은 추가 형식을 기본적으로 지원합니다.

 

  • Console.WriteLine(JsonSerializer.Serialize(
        [ Half.MaxValue, Int128.MaxValue, UInt128.MaxValue ]
    ));
    // [65500,170141183460469231731687303715884105727,340282366920938463463374607431768211455]
    
  • Memory<T>ReadOnlyMemory<T> 값입니다. byte 값은 Base64 문자열로 직렬화되고 다른 형식은 JSON 배열로 직렬화됩니다.
  • C#

 

  • JsonSerializer.Serialize<ReadOnlyMemory<byte>>(new byte[] { 1, 2, 3 }); // "AQID"
    JsonSerializer.Serialize<Memory<int>>(new int[] { 1, 2, 3 }); // [1,2,3]
    

원본 생성기

.NET 8에는 리플렉션 기반 직렬 변환기와 동등한 네이티브 AOT 환경을 만들기 위한 System.Text.Json 원본 생성기의 향상된 기능이 포함되어 있습니다. 예시:

  • 원본 생성기는 이제 형식 requiredinit 속성을 직렬화하도록 지원합니다. 모두 리플렉션 기반 serialization에서 이미 지원되었습니다.
  • 소스 생성 코드의 서식이 향상되었습니다.
  • JsonSourceGenerationOptionsAttribute 를 사용한 JsonSerializerOptions기능 패리티 자세한 내용은 지정 옵션(원본 생성)을 참조하세요.
  • 추가 진단(예: SYSLIB1034SYSLIB1039).
  • 무시되거나 액세스할 수 없는 속성 형식은 포함하지 마세요.
  • 임의 형식 종류 내에서 중첩 JsonSerializerContext 선언을 지원합니다.
  • 약한 형식의 소스 생성 시나리오에서 컴파일러에서 생성되거나 말할 수 없는 형식을 지원합니다. 컴파일러에서 생성된 형식은 원본 생성기 System.Text.Json 에서 명시적으로 지정할 수 없으므로 이제 런타임에 가장 가까운 상위 항목 확인을 수행합니다. 이 해상도는 값을 serialize할 가장 적절한 슈퍼 형식을 결정합니다.
  • 새 변환기 유형 JsonStringEnumConverter<TEnum>. 기존 JsonStringEnumConverter 클래스는 Native AOT에서 지원되지 않습니다. 다음과 같이 열거형 형식에 주석을 달 수 있습니다.
  • C#

 

  • [JsonConverter(typeof(JsonStringEnumConverter<MyEnum>))]
    public enum MyEnum { Value1, Value2, Value3 }
    
    [JsonSerializable(typeof(MyEnum))]
    public partial class MyContext : JsonSerializerContext { }
    
    자세한 내용은 열거형 필드를 문자열로 Serialize를 참조 하세요.
  • 새 JsonConverter.Type 속성을 사용하면 제네릭 JsonConverter 이 아닌 인스턴스의 형식을 조회할 수 있습니다.
  • C#

 

  • Dictionary<Type, JsonConverter> CreateDictionary(IEnumerable<JsonConverter> converters)
        => converters.Where(converter => converter.Type != null)
                     .ToDictionary(converter => converter.Type!);
    
    인스턴스 및 typeof(T)JsonConverter<T> 인스턴스에 대해 JsonConverterFactory 반환 null 되므로 이 속성은 null을 허용합니다.
원본 생성기 연결

클래스에는 JsonSerializerOptions 기존 TypeInfoResolver 속성을 보완하는 새 TypeInfoResolverChain 속성이 포함됩니다. 이러한 속성은 소스 생성기를 연결하기 위한 계약 사용자 지정에 사용됩니다. 새 속성을 추가하면 하나의 호출 사이트에서 연결된 구성 요소를 모두 지정할 필요가 없습니다. 이 구성 요소는 팩트 후에 추가할 수 있습니다. TypeInfoResolverChain 또한 체인을 소개하거나 해당 체인에서 구성 요소를 제거할 수 있습니다. 자세한 내용은 원본 생성기 결합을 참조 하세요.

또한 JsonSerializerOptions.AddContext<TContext>() 이제 사용되지 않습니다. 그것은 및 TypeInfoResolverChain 속성으로 TypeInfoResolver 대체되었습니다. 자세한 내용은 SYSLIB0049 참조하세요.

인터페이스 계층 구조

.NET 8은 인터페이스 계층에서 속성을 직렬화하는 지원을 추가합니다.

다음 코드는 즉시 구현된 인터페이스와 해당 기본 인터페이스의 속성이 직렬화되는 예제를 보여줍니다.

C#
IDerived value = new DerivedImplement { Base = 0, Derived = 1 };
JsonSerializer.Serialize(value);
// {"Base":0,"Derived":1}

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에는 (밑줄 포함) 및 kebab-case (하이픈 포함) 속성 이름 변환에 대한 snake_case 새 명명 정책이 포함되어 있습니다. 기존 JsonNamingPolicy.CamelCase 정책과 유사하게 다음 정책을 사용합니다.

C#
var options = new JsonSerializerOptions
{
    PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower
};
JsonSerializer.Serialize(new { PropertyName = "value" }, options);
// { "property_name" : "value" }

자세한 내용은 기본 제공 명명 정책 사용을 참조 하세요.

읽기 전용 속성

이제 읽기 전용 필드 또는 속성(즉, 접근자가 없는 set 필드)으로 역직렬화할 수 있습니다.

이 지원을 전역적으로 옵트인하려면 새 옵션을 PreferredObjectCreationHandlingJsonObjectCreationHandling.Populate>로 설정합니다. 호환성이 중요한 경우 속성을 채울 특정 형식 또는 개별 속성에 특성을 배치 [JsonObjectCreationHandling(JsonObjectCreationHandling.Populate)] 하여 기능을 보다 세분화할 수도 있습니다.

예를 들어 두 개의 읽기 전용 속성이 있는 형식으로 CustomerInfo 역직렬화하는 다음 코드를 고려해 보세요.

C#
using System.Text.Json;

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":null}}

역직렬화 동작 채우기에 대한 자세한 내용은 초기화된 속성 채우기를 참조하세요.

리플렉션 기반 기본값 사용 안 함

이제 기본적으로 리플렉션 기반 직렬 변환기를 사용하지 않도록 설정할 수 있습니다. 이 비활성화는 특히 트리밍 및 네이티브 AOT 앱에서 사용되지 않는 리플렉션 구성 요소의 우발적인 루팅을 방지하는 데 유용합니다. 인수를 serialization 및 deserialization 메서드에 전달 JsonSerializer 하도록 요구하여 JsonSerializerOptions 기본 리플렉션 기반 serialization을 사용하지 않도록 설정하려면 MSBuild 속성을 false 프로젝트 파일로 설정합니다JsonSerializerIsReflectionEnabledByDefault.

IsReflectionEnabledByDefault API를 사용하여 기능 스위치의 값을 검사. 라이브러리 작성자 빌드를 기반으로 System.Text.Json하는 경우 리플렉션 구성 요소를 실수로 루팅하지 않고도 속성을 사용하여 기본값을 구성할 수 있습니다.

자세한 내용은 리플렉션 기본값 사용 안 함을 참조 하세요.

새 JsonNode API 메서드

System.Text.Json.Nodes.JsonArray 형식에는 JsonNode 다음과 같은 새 메서드가 포함됩니다.

C#
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>();
}

비공용 멤버

지정된 형식 및 JsonConstructorAttribute 특성 주석을 사용하여 JsonIncludeAttribute 비공용 멤버를 serialization 계약에 옵트인할 수 있습니다.

C#
string json = JsonSerializer.Serialize(new MyPoco(42));
// {"X":42}

JsonSerializer.Deserialize<MyPoco>(json);

public class MyPoco
{
    [JsonConstructor]
    internal MyPoco(int x) => X = x;

    [JsonInclude]
    internal int X { get; }
}

자세한 내용은 변경할 수 없는 형식 및 공용이 아닌 멤버 및 접근자 사용을 참조하세요.

스트리밍 역직렬화 API

.NET 8에는 예를 들어 GetFromJsonAsAsyncEnumerableIAsyncEnumerable<T> 스트리밍 역직렬화 확장 메서드가 포함되어 있습니다. 예를 들어 HttpClientJsonExtensions.GetFromJsonAsync반환Task<TResult>하는 유사한 메서드가 있습니다. 새 확장 메서드는 스트리밍 API를 호출하고 반환 IAsyncEnumerable<T>합니다.

다음 코드에서는 새 확장 메서드를 사용하는 방법을 보여 줍니다.

C#
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 계약을 쉽게 수정할 수 있습니다.

C#
var options = new JsonSerializerOptions
{
    TypeInfoResolver = MyContext.Default
        .WithAddedModifier(static typeInfo =>
        {
            foreach (JsonPropertyInfo prop in typeInfo.Properties)
            {
                prop.Name = prop.Name.ToUpperInvariant();
            }
        })
};

새 JsonContent.오버로드 만들기

이제 트리밍 안전 또는 원본 생성 계약을 사용하여 인스턴스를 만들 JsonContent 수 있습니다. 새 메서드는 다음과 같습니다.

C#
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()
  • 이 오버로드는 트리밍이 안전하도록 설계되었으므로 옵션 인스턴스가 확인자를 사용하여 구성되지 않은 경우 예외를 throw합니다.
  • JsonSerializerOptions.MakeReadOnly(Boolean)
  • 이 오버로드에 전달 true 하면 옵션 인스턴스가 누락된 경우 기본 리플렉션 확인자를 사용하여 해당 인스턴스를 채웁니다. 이 메서드는 표시 RequiresUnreferenceCode/RequiresDynamicCode 되어 있으므로 네이티브 AOT 애플리케이션에 적합하지 않습니다.

IsReadOnly 속성을 사용하면 옵션 인스턴스가 고정된 경우 검사 수 있습니다.

시간 추상화

TimeProvider 클래스 및 ITimer 인터페이스는 테스트 시나리오에서 시간을 모의할 수 있는 시간 추상화 기능을 추가합니다. 또한 시간 추상화 기능을 사용하여 시간 진행을 사용하는 모 Task 의 작업을 사용할 Task.Delay 수 있습니다 Task.WaitAsync. 시간 추상화는 다음과 같은 필수 시간 작업을 지원합니다.

  • 로컬 및 UTC 시간 검색
  • 성능 측정을 위한 타임스탬프 가져오기
  • 타이머 만들기

다음 코드 조각은 몇 가지 사용 예제를 보여 줍니다.

C#
// Get system time.
DateTimeOffset utcNow = TimeProvider.System.GetUtcNow();
DateTimeOffset localNow = TimeProvider.System.GetLocalNow();

// Create a time provider that works with a
// time zone that's different than the local time zone.
private class ZonedTimeProvider : TimeProvider
{
    private TimeZoneInfo _zoneInfo;

    public ZonedTimeProvider(TimeZoneInfo zoneInfo) : base()
    {
        _zoneInfo = zoneInfo ?? TimeZoneInfo.Local;
    }

    public override TimeZoneInfo LocalTimeZone => _zoneInfo;

    public static TimeProvider FromLocalTimeZone(TimeZoneInfo zoneInfo) =>
        new ZonedTimeProvider(zoneInfo);
}

// Create a timer using a time provider.
ITimer timer = timeProvider.CreateTimer(
    callBack, state, delay, Timeout.InfiniteTimeSpan);

// Measure a period using the system time provider.
long providerTimestamp1 = TimeProvider.System.GetTimestamp();
long providerTimestamp2 = TimeProvider.System.GetTimestamp();

var period = GetElapsedTime(providerTimestamp1, providerTimestamp2);

UTF8 개선 사항

대상 범위에 형식의 문자열과 유사한 표현을 쓸 수 있도록 하려면 형식에 새 IUtf8SpanFormattable 인터페이스를 구현합니다. 이 새 인터페이스는 밀접하게 관련 ISpanFormattable되어 있지만 UTF16 및 Span<byte> 대신 UTF8을 Span<char>대상으로 합니다.

IUtf8SpanFormattable은 대상 stringSpan<char>Span<byte>지정 여부와 정확히 동일한 공유 논리를 사용하여 모든 기본 형식(기타 형식)에서 구현되었습니다. 모든 형식(새 "B" 이진 지정자 포함) 및 모든 문화권에 대한 모든 지원을 제공합니다. 즉, 이제 , ,, , UInt32DateTimeDateOnlyDateTimeOffsetComplexIntPtrNFloatInt128SByteInt32UInt128Int16UIntPtrDoubleDecimalGuidCharHalfIPNetworkIPAddressInt64TimeSpanUInt64SingleRuneTimeOnlyUInt16,, 및 Version에서 ByteUTF8로 직접 서식을 지정할 수 있습니다.

Utf8.TryWrite 메서드는 UTF16 기반의 기존 MemoryExtensions.TryWrite 메서드에 UTF8 기반 대응 메서드를 제공합니다. 보간된 문자열 구문을 사용하여 복합 식의 형식을 UTF8 바이트 범위로 직접 지정할 수 있습니다. 예를 들면 다음과 같습니다.

C#
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.TryGetChars(ReadOnlySpan<Byte>, Span<Char>, Int32) 함께 대상 범위로 인코딩 및 디코딩을 지원하는 새 Encoding.TryGetBytes(ReadOnlySpan<Char>, Span<Byte>, Int32) 메서드를 활용합니다. 범위가 결과 상태를 보유하기에 충분하지 않은 경우 메서드는 예외를 throw하지 않고 반환 false 합니다.

임의성 작업 방법

System.Security.Cryptography.RandomNumberGenerator 형식은 임의 System.Random 성 작업을 위한 두 가지 새로운 메서드를 도입합니다.

GetItems<T>()

System.Random.GetItems 항목과 System.Security.Cryptography.RandomNumberGenerator.GetItems 메서드를 사용하면 입력 집합에서 지정된 수의 항목을 임의로 선택할 수 있습니다. 다음 예제에서는 (속성에서 제공 하는 Random.Shared 인스턴스에서) 배열에 31 항목을 임의로 삽입 하는 방법을 System.Random.GetItems<T>() 보여 줍니다. 이 예제는 플레이어가 색이 지정된 단추 시퀀스를 기억해야 하는 "Simon" 게임에서 사용할 수 있습니다.

C#
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>) 메서드를 사용하면 범위의 순서를 임의로 지정할 수 있습니다. 이러한 방법은 기계 학습에서 학습 편향을 줄이는 데 유용합니다(따라서 첫 번째 방법은 항상 학습이 아니며 마지막 방법은 항상 테스트하는 것이 아닙니다).

C#
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())을 수행할 수 있습니다. 이러한 형식은 처음 사용할 때 채워진 다음 수명이 긴 서비스 기간 동안 유지되는 컬렉션에 특히 유용합니다. 예를 들면 다음과 같습니다.
  • C#

 

  • private static readonly FrozenDictionary<string, bool> s_configurationData =
        LoadConfigurationData().ToFrozenDictionary(optimizeForReads: true);
    
    // ...
    if (s_configurationData.TryGetValue(key, out bool setting) && setting)
    {
        Process();
    }
    
  • System.Buffers.SearchValues<T> 형식은 전달된 컬렉션에서 값의 첫 번째 발생을 찾는 메서드에 전달되도록 설계되었습니다. 예를 들어 String.IndexOfAny(Char[]) 호출된 배열의 지정된 배열에서 string 문자가 처음 나타나는 경우를 찾습니다. NET 8은 새 형식의 인스턴스를 허용하는 메서드와 MemoryExtensions.IndexOfAny 같은 String.IndexOfAny 메서드의 새 오버로드를 추가합니다. 인스턴스System.Buffers.SearchValues<T>를 만들 때 후속 검색을 최적화하는 데 필요한 모든 데이터가 해당 시간에 파생되므로 작업이 미리 수행됩니다.
  • System.Text.CompositeFormat 형식은 컴파일 시간에 알려지지 않은 형식 문자열을 최적화하는 데 유용합니다(예: 리소스 파일에서 형식 문자열이 로드되는 경우). 문자열 구문 분석과 같은 작업을 수행하기 위해 약간의 추가 시간이 소요되지만 각 사용 시 작업이 수행되지 않도록 합니다.
  • C#

 

  • 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.Runtime.Intrinsics 네임스페이스의 향상된 기능에 대해 System.Numerics 설명합니다.

  • Vector256<T>. Matrix3x2Matrix4x4 NET 8에서 하드웨어 가속이 향상되었습니다. 예를 들어 가능한 Vector256<T> 경우 내부적으로 작업이 되도록 2x Vector128<T> 다시 구현되었습니다. 이렇게 하면 Arm64와 같이 일부 함수를 Vector128.IsHardwareAccelerated == true 부분적으로 가속할 수 있습니다 Vector256.IsHardwareAccelerated == false.
  • 이제 하드웨어 내장 함수에 특성이 주석으로 ConstExpected 추가됩니다. 이렇게 하면 기본 하드웨어에 상수가 필요할 때와 상수가 아닌 값이 예기치 않게 성능에 저하될 수 있는 경우를 사용자가 인식할 수 있습니다.
  • API가 Lerp(TSelf, TSelf, TSelf)Lerp (), (Double) double 및 Half.Single에 추가 floatIFloatingPointIeee754<TSelf> 되었습니다. 이 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 추가합니다.

이러한 클래스는 64비트 프로세스에서만 사용할 수 있는 명령에 대한 속성 및 중첩 클래스 Avx512F.X64 를 노출 IsSupported 한다는 측면에서 다른 ISA(명령 집합 아키텍처)와 동일한 일반 셰이프를 따릅니다. 또한 각 클래스에는 해당 명령 집합에 Avx512VL 대한 (벡터 길이) 확장을 노출하는 중첩 클래스 Avx512F.VL 가 있습니다.

코드에서 -specific 또는 Avx512F-specific 지침을 명시적으로 사용하지 Vector512않더라도 새 AVX-512 지원을 계속 활용할 수 있습니다. JIT는 사용 Vector128<T> 하거나 Vector256<T>사용할 때 암시적으로 추가 레지스터 및 지침을 활용할 수 있습니다. 기본 클래스 라이브러리는 기본 형식에 대해 노출된 Span<T> 대부분의 연산 및 ReadOnlySpan<T> 많은 수학 API에서 내부적으로 이러한 하드웨어 내장 함수를 사용합니다.

데이터 유효성 검사

네임스페이스에는 System.ComponentModel.DataAnnotations 클라우드 네이티브 서비스의 유효성 검사 시나리오를 위한 새 데이터 유효성 검사 특성이 포함됩니다. 기존 DataAnnotations 유효성 검사기는 양식의 필드와 같은 일반적인 UI 데이터 항목 유효성 검사에 맞게 조정되지만 새 특성은 구성 옵션과 같은 비 사용자 항목 데이터의 유효성을 검사하도록 설계되었습니다. 새 특성 외에도 새 속성이 및 RequiredAttribute 형식에 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를 사용하면 키-값 쌍 태그를 만들 때 개체에 MeterInstrument 연결할 수 있습니다. 게시된 메트릭 측정값의 집계자는 태그를 사용하여 집계된 값을 구분할 수 있습니다.

C#
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);

Instrument instrument = meter.CreateCounter<int>(
    "counter", null, null, new TagList() { { "counterKey1", "counterValue1" } }
);
instrument.Add(1);

새 API의 구성 내용:

암호화

.NET 8은 SHA-3 해시 기본 형식에 대한 지원을 추가합니다. (SHA-3은 현재 OpenSSL 1.1.1 이상 및 Windows 11 빌드 25324 이상을 사용하는 Linux에서 지원됩니다.) SHA-2를 사용할 수 있는 API는 이제 SHA-3 칭찬을 제공합니다. 여기에는 해시, SHA3_512 HMAC HashAlgorithmName.SHA3_512HashAlgorithmName.SHA3_384HashAlgorithmName.SHA3_256, HMACSHA3_384HMACSHA3_256및 HMACSHA3_512 알고리즘을 구성할 수 RSAEncryptionPadding.OaepSHA3_256RSAEncryptionPadding.OaepSHA3_512RSAEncryptionPadding.OaepSHA3_384있는 해시 및 RSA OAEP 암호화에 대한 해시가 포함SHA3_256SHA3_384됩니다.

다음 예제에서는 속성을 포함하여 API를 사용하여 플랫폼이 SHA3_256.IsSupported SHA-3을 지원하는지 확인하는 방법을 보여 줍니다.

C#
// 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 오버로드를 사용하면 압축된 파일이 포함된 스트림을 제공하고 해당 내용을 파일 시스템에 추출할 수 있습니다. 새 오버로드는 다음과 같습니다.

C#
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는 디스크 공간을 제한할 때 디스크를 중간 단계로 사용할 필요가 없으므로 유용할 수 있습니다.

확장 라이브러리

이 섹션에는 다음 하위 항목이 포함되어 있습니다.

키 지정된 DI 서비스

DI(키 종속성 주입) 서비스는 키를 사용하여 DI 서비스를 등록하고 검색하는 방법을 제공합니다. 키를 사용하여 서비스를 등록하고 사용하는 방법의 범위를 지정할 수 있습니다. 다음은 몇 가지 새로운 API입니다.

다음 예제에서는 키 지정된 DI 서비스를 사용하는 방법을 보여 줍니다.

C#
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddSingleton<BigCacheConsumer>();
builder.Services.AddSingleton<SmallCacheConsumer>();
builder.Services.AddKeyedSingleton<ICache, BigCache>("big");
builder.Services.AddKeyedSingleton<ICache, SmallCache>("small");
var 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을 참조하세요.

호스트된 수명 주기 서비스

이제 호스트된 서비스에는 애플리케이션 수명 주기 동안 더 많은 실행 옵션이 있습니다. IHostedServiceStartAsyncStopAsyncIHostedLifecycleService 는 다음과 같은 추가 메서드를 제공합니다.

이러한 메서드는 각각 기존 지점 전후에 실행됩니다.

다음 예제에서는 새 API를 사용하는 방법을 보여 줍니다.

C#
using Microsoft.Extensions.Hosting;

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을 참조 하세요.

옵션 유효성 검사

원본 생성기

시작 오버헤드를 줄이고 유효성 검사 기능 집합을 개선하기 위해 유효성 검사 논리를 구현하는 소스 코드 생성기를 도입했습니다. 다음 코드에서는 예제 모델 및 유효성 검사기 클래스를 보여 줍니다.

C#
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>
{
}

앱에서 종속성 주입을 사용하는 경우 다음 예제 코드와 같이 유효성 검사를 삽입할 수 있습니다.

C#
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllersWithViews();
builder.Services.Configure<FirstModelNoNamespace>(
    builder.Configuration.GetSection(...));

builder.Services.AddSingleton<
    IValidateOptions<FirstModelNoNamespace>, FirstValidatorNoNamespace>();
builder.Services.AddSingleton<
    IValidateOptions<SecondModelNoNamespace>, SecondValidatorNoNamespace>();

ValidateOptionsResultBuilder 형식

.NET 8에서는 개체를 ValidateOptionsResultBuilder 쉽게 만들 수 있도록 형식을 도입했습니다 ValidateOptionsResult . 중요한 것은 이 작성기에서 여러 오류가 누적되는 것을 허용합니다. 이전에는 구현 IValidateOptions<TOptions>.Validate(String, TOptions) 하는 데 필요한 개체를 만드는 ValidateOptionsResult 것이 어려웠으며 경우에 따라 계층화된 유효성 검사 오류가 발생했습니다. 여러 오류가 있는 경우 첫 번째 오류에서 유효성 검사 프로세스가 중지되는 경우가 많습니다.

다음 코드 조각은 .의 ValidateOptionsResultBuilder사용 예제를 보여 줍니다.

C#
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를 제공하지 않으면 시스템에서 자동으로 생성됩니다.

C#
public LoggerMessageAttribute(LogLevel level, string message);
public LoggerMessageAttribute(LogLevel level);
public LoggerMessageAttribute(string message);

확장 메트릭

IMeterFactory 인터페이스

DI(종속성 주입) 컨테이너에 새 IMeterFactory 인터페이스를 등록하고 이를 사용하여 격리된 방식으로 개체를 만들 Meter 수 있습니다.

IMeterFactory 기본 미터 팩터리 구현을 사용하여 DI 컨테이너에 등록합니다.

C#
// 'services' is the DI IServiceCollection.
services.AddMetrics();

그런 다음 소비자는 미터 팩터리를 가져와서 새 Meter 개체를 만드는 데 사용할 수 있습니다.

C#
IMeterFactory meterFactory = serviceProvider.GetRequiredService<IMeterFactory>();

MeterOptions options = new MeterOptions("MeterName")
{
    Version = "version",
};

Meter meter = meterFactory.Create(options);

MetricCollector<T> 클래스

MetricCollector<T> 클래스를 사용하면 타임스탬프와 함께 메트릭 측정값을 기록할 수 있습니다. 또한 클래스는 정확한 타임스탬프 생성을 위해 선택한 시간 공급자를 유연하게 사용할 수 있습니다.

C#
const string CounterName = "MyCounter";

var now = DateTimeOffset.Now;

var timeProvider = new FakeTimeProvider(now);
using var meter = new Meter(Guid.NewGuid().ToString());
var counter = meter.CreateCounter<long>(CounterName);
using var collector = new MetricCollector<long>(counter, timeProvider);

Assert.Empty(collector.GetMeasurementSnapshot());
Assert.Null(collector.LastMeasurement);

counter. Add(3);

// Verify the update was recorded.
Assert.Equal(counter, collector.Instrument);
Assert.NotNull(collector.LastMeasurement);

Assert.Single(collector.GetMeasurementSnapshot());
Assert.Same(collector.GetMeasurementSnapshot().Last(), collector.LastMeasurement);
Assert.Equal(3, collector.LastMeasurement.Value);
Assert.Empty(collector.LastMeasurement.Tags);
Assert.Equal(now, collector.LastMeasurement.Timestamp);

System.Numerics.Tensors.TensorPrimitives

업데이트된 System.Numerics.Tensors NuGet 패키지에는 텐서 작업에 대한 지원을 추가하는 API가 새 TensorPrimitives 네임스페이스에 포함되어 있습니다. 텐서 기본 형식은 AI 및 기계 학습과 같은 데이터 집약적 워크로드를 최적화합니다.

의미 체계 검색 및 RAG(검색 보강 세대)와 같은 AI 워크로드는 관련 데이터로 프롬프트를 보강하여 ChatGPT와 같은 대규모 언어 모델의 자연어 기능을 확장합니다. 이러한 워크로드의 경우 질문에 대답하기 위해 가장 관련성이 큰 데이터를 찾기 위한 코사인 유사성과 같은 벡터에 대한 작업이 매우 중요합니다. System.Numerics.Tensors.TensorPrimitives 패키지는 벡터 작업에 API를 제공하므로 외부 종속성을 사용하거나 고유한 구현을 작성할 필요가 없습니다.

이 패키지는 System.Numerics.Tensors 패키지를 대체합니다.

자세한 내용은 .NET 8 RC 2 블로그 게시물 발표를 참조 하세요.

가비지 수집

.NET 8은 즉시 메모리 제한을 조정하는 기능을 추가합니다. 이는 수요가 오고 가는 클라우드 서비스 시나리오에서 유용합니다. 비용 효율성을 위해 서비스는 수요가 변동함에 따라 리소스 소비를 확장 및 축소해야 합니다. 서비스가 수요 감소를 감지하면 메모리 제한을 줄여 리소스 사용량을 축소할 수 있습니다. 이전에는 GC(가비지 수집기)가 변경 내용을 인식하지 못하고 새 제한보다 더 많은 메모리를 할당할 수 있으므로 실패했습니다. 이 변경으로 API를 RefreshMemoryLimit() 호출하여 새 메모리 제한으로 GC를 업데이트할 수 있습니다.

주의해야 할 몇 가지 제한 사항은 다음과 같습니다.

  • 32비트 플랫폼(예: Windows x86 및 Linux ARM)에서 .NET은 아직 없는 경우 새 힙 하드 제한을 설정할 수 없습니다.
  • API는 새로 고침 실패를 나타내는 0이 아닌 상태 코드를 반환할 수 있습니다. 규모 축소가 너무 공격적이고 GC가 기동할 여지가 없는 경우에 발생할 수 있습니다. 이 경우 호출 GC.Collect(2, GCCollectionMode.Aggressive) 을 사용하여 현재 메모리 사용량을 축소한 다음 다시 시도하세요.
  • GC가 시작 RefreshMemoryLimit 중에 프로세스가 처리할 수 있다고 생각되는 크기를 초과하여 메모리 제한을 확장하면 호출이 성공하지만 제한으로 인식되는 것보다 더 많은 메모리를 사용할 수는 없습니다.

다음 코드 조각은 API를 호출하는 방법을 보여줍니다.

C#
GC.RefreshMemoryLimit();

메모리 제한과 관련된 일부 GC 구성 설정을 새로 고칠 수도 있습니다. 다음 코드 조각은 힙 하드 제한을 100 mebibytes(MiB)로 설정합니다.

C#
AppContext.SetData("GCHeapHardLimit", (ulong)100 * 1_024 * 1_024);
GC.RefreshMemoryLimit();

API는 하드 제한이 유효하지 않은 경우(예: 음수 힙 하드 제한 백분율의 경우) 하드 제한이 너무 낮은 경우를 throw InvalidOperationException 할 수 있습니다. 이는 새 AppData 설정으로 인해 또는 컨테이너 메모리 제한 변경으로 인해 새로 고침이 설정되는 힙 하드 제한이 이미 커밋된 것보다 낮은 경우에 발생할 수 있습니다.

구성 바인딩 원본 생성기

.NET 8은 ASP.NET Core에서 AOT 및 트리밍 친화적 구성 을 제공하는 원본 생성기를 도입했습니다. 생성기는 기존 리플렉션 기반 구현의 대안입니다.

원본 생성기는 형식 정보를 검색하기 위한 호출에 대해 Configure(TOptions)검색 Bind합니다 Get . 프로젝트에서 생성기를 사용하도록 설정하면 컴파일러는 기존 리플렉션 기반 프레임워크 구현을 통해 생성된 메서드를 암시적으로 선택합니다.

생성기를 사용하기 위해 소스 코드 변경이 필요하지 않습니다. 기본적으로 AOT 웹앱에서 사용하도록 설정됩니다. 다른 프로젝트 형식의 경우 원본 생성기는 기본적으로 꺼져 있지만 프로젝트 파일에서 속성을 설정 EnableConfigurationBindingGenerator 하여 옵트인할 true 수 있습니다.

XML
<PropertyGroup>
    <EnableConfigurationBindingGenerator>true</EnableConfigurationBindingGenerator>
</PropertyGroup>

다음 코드는 바인더를 호출하는 예제를 보여줍니다.

C#
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;

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 MyOptions();
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 5에서 도입되었지만 리플렉션에 대한 해당 지원은 당시에 추가되지 않았습니다. 함수 포인터를 사용 typeof 하거나 리플렉션할 때(예: typeof(delegate*<void>())FieldInfo.FieldType 각각) IntPtr 반환되었습니다. .NET 8부터 개체가 System.Type 대신 반환됩니다. 이 형식은 호출 규칙, 반환 형식 및 매개 변수를 포함하여 함수 포인터 메타데이터에 대한 액세스를 제공합니다.

참고

함수에 대한 실제 주소인 함수 포인터 인스턴스는 계속해서 으로 IntPtr표시됩니다. 리플렉션 형식만 변경되었습니다.

새 기능은 현재 CoreCLR 런타임 및 .에서만 구현됩니다 MetadataLoadContext.

새 API가 추가System.Type되었습니다(예: IsFunctionPointer/>).System.Reflection.ParameterInfoSystem.Reflection.PropertyInfoSystem.Reflection.FieldInfo 다음 코드에서는 리플렉션에 새 API 중 일부를 사용하는 방법을 보여 줍니다.

C#
// Sample class that contains a function pointer field.
public unsafe class UClass
{
    public delegate* unmanaged[Cdecl, SuppressGCTransition]<in int, void> _fp;
}

// ...

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()}");

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;

// 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.
var modifiers =
    modifiedType.GetFunctionPointerParameterTypes()[0].GetRequiredCustomModifiers();

foreach (Type modreq in modifiers)
{
    Console.WriteLine($"Required modifier for first parameter: {modreq}");
}

이전 예제에서는 다음 출력을 생성합니다.

Output
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

네이티브 AOT 지원

네이티브 AOT로 게시하는 옵션은 .NET 7에서 처음 도입되었습니다. Native AOT를 사용하여 앱을 게시하면 런타임이 필요하지 않은 완전히 자체 포함된 버전의 앱이 만들어집니다. 모든 것이 단일 파일에 포함됩니다. .NET 8은 네이티브 AOT 게시에 다음과 같은 개선 사항을 제공합니다.

  • macOS에서 x64 및 Arm64 아키텍처에 대한 지원을 추가합니다.
  • Linux에서 네이티브 AOT 앱의 크기를 최대 50%까지 줄입니다. 다음 표에서는 .NET 7과 .NET 8의 전체 .NET 런타임을 포함하는 네이티브 AOT를 사용하여 게시된 "헬로 월드" 앱의 크기를 보여줍니다.
  •  
  • 운영 체제 .NET 7 .NET 8
    Linux x64(포함 -p:StripSymbols=true) 3.76MB 1.84MB
    Windows x64 2.85MB 1.77MB
  • 최적화 기본 설정(크기 또는 속도)을 지정할 수 있습니다. 기본적으로 컴파일러는 애플리케이션의 크기를 염두에 두고 빠른 코드를 생성하도록 선택합니다. 그러나 MSBuild 속성을 사용하여 <OptimizationPreference> 하나 또는 다른 항목에 대해 특별히 최적화할 수 있습니다. 자세한 내용은 AOT 배포 최적화를 참조 하세요.

콘솔 앱 템플릿

이제 기본 콘솔 앱 템플릿에 AOT 기본 제공 지원이 포함됩니다. AOT 컴파일용으로 구성된 프로젝트를 만들려면 실행 dotnet new console --aot하기만 하면됩니다. 추가된 --aot 프로젝트 구성에는 다음 세 가지 효과가 있습니다.

  • 프로젝트를 게시할 때(예: Visual Studio와 함께) Native AOT를 dotnet publish 사용하여 네이티브 자체 포함 실행 파일을 생성합니다.
  • 트리밍, AOT 및 단일 파일에 대한 호환성 분석기를 사용하도록 설정합니다. 이러한 분석기는 프로젝트의 문제가 있을 수 있는 부분에 대해 경고합니다(있는 경우).
  • AOT 컴파일 없이 프로젝트를 디버그할 때 AOT와 비슷한 환경을 얻을 수 있도록 AOT의 디버그 시간 에뮬레이션을 사용하도록 설정합니다. 예를 들어 AOT에 주석이 추가되지 않았으므로 호환성 분석기에서 누락된 NuGet 패키지에서 사용하는 System.Reflection.Emit 경우 에뮬레이션은 AOT를 사용하여 프로젝트를 게시하려고 할 때 놀랄 일이 없다는 것을 의미합니다.

네이티브 AOT를 사용하여 iOS와 유사한 플랫폼 대상 지정

.NET 8은 iOS와 유사한 플랫폼에 대해 네이티브 AOT 지원을 사용하도록 설정하는 작업을 시작합니다. 이제 다음 플랫폼에서 Native AOT를 사용하여 .NET iOS 및 .NET MAUI 애플리케이션을 빌드하고 실행할 수 있습니다.

  • ios
  • iossimulator
  • maccatalyst
  • tvos
  • tvossimulator

예비 테스트에 따르면 Mono 대신 Native AOT를 사용하는 .NET iOS 앱의 경우 디스크의 앱 크기가 약 35% 감소합니다. .NET MAUI iOS 앱용 디스크의 앱 크기는 최대 50%까지 감소합니다. 또한 시작 시간도 더 빠릅니다. .NET iOS 앱의 시작 시간은 약 28% 더 빨라진 반면. .NET MAUI iOS 앱은 Mono에 비해 약 50% 더 나은 시작 성능을 갖습니다. .NET 8 지원은 실험적이며 기능 전체의 첫 번째 단계일 뿐입니다. 자세한 내용은 .NET MAUI 블로그 게시물의 .NET 8 성능 향상을 참조하세요.

네이티브 AOT 지원은 앱 배포를 위한 옵트인 기능으로 사용할 수 있습니다. Mono는 여전히 앱 개발 및 배포의 기본 런타임입니다. iOS 디바이스에서 Native AOT를 사용하여 .NET MAUI 애플리케이션을 빌드하고 실행하려면 .NET MAUI 워크로드를 설치하고 dotnet new maui -n HelloMaui 앱을 만드는 데 사용합니다dotnet workload install maui. 그런 다음 프로젝트 파일에서 MSBuild 속성을 PublishAottrue 설정합니다.

XML
<PropertyGroup>
  <PublishAot>true</PublishAot>
</PropertyGroup>

다음 예제와 같이 필수 속성을 설정하고 실행 dotnet publish 하면 네이티브 AOT를 사용하여 앱이 배포됩니다.

.NET CLI
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 프레임워크와의 호환성이 제한됩니다.

성능 개선 사항

.NET 8에는 코드 생성 및 JIT(Just-In-Time) 컴파일 기능이 향상되었습니다.

  • Arm64 성능 향상
  • SIMD 개선 사항
  • AVX-512 ISA 확장 지원(Vector512 및 AVX-512 참조)
  • 클라우드 네이티브 개선 사항
  • JIT 처리량 향상
  • 루프 및 일반 최적화
  • 다음으로 표시된 필드에 대한 액세스가 최적화됨 ThreadStaticAttribute
  • 연속 레지스터 할당입니다. Arm64에는 튜플 피연산자에서 모든 엔터티가 연속 레지스터에 있어야 하는 테이블 벡터 조회에 대한 두 가지 지침이 있습니다.
  • 이제 JIT/NativeAOT는 컴파일 시간에 크기를 확인할 수 있는 경우 비교, 복사 및 0과 같은 SIMD를 사용하여 일부 메모리 작업의 등록을 취소하고 자동 벡터화할 수 있습니다.

또한 동적 PGO(프로필 기반 최적화)가 개선되었으며 이제 기본적으로 사용하도록 설정됩니다. 더 이상 런타임 구성 옵션을 사용하여 사용하도록 설정할 필요가 없습니다. 동적 PGO는 계층화된 컴파일과 함께 작동하여 계층 0 중에 배치되는 추가 계측에 따라 코드를 더욱 최적화합니다.

평균적으로 동적 PGO는 성능을 약 15% 증가합니다. 최대 4,600개 테스트의 벤치마크 제품군에서 23%는 20% 이상의 성능 향상을 보였습니다.

Codegen 구조체 승격

.NET 8에는 구조체 변수를 승격하는 JIT의 기능을 일반화하는 codegen에 대한 새로운 물리적 승격 최적화 패스가 포함되어 있습니다. 이 최적화(집계의 스칼라 대체라고도 함)는 구조체 변수의 필드를 JIT가 추론하고 보다 정확하게 최적화할 수 있는 기본 변수로 대체합니다.

JIT는 이미 이 최적화를 지원했지만 다음을 비롯한 몇 가지 큰 제한 사항이 있습니다.

  • 4개 이하의 필드가 있는 구조체에 대해서만 지원되었습니다.
  • 각 필드가 기본 형식이거나 기본 형식을 래핑하는 간단한 구조체인 경우에만 지원되었습니다.

물리적 승격은 이러한 제한을 제거하여 여러 가지 오랜 JIT 문제를 해결합니다.

.NET MAUI

.NET 8의 .NET MAUI의 새로운 기능과 관련하여 자세한 내용은 .NET 8용 .NET MAUI의 새로운 기능입니다.

.NET SDK

이 섹션에는 다음 하위 항목이 포함되어 있습니다.

CLI 기반 프로젝트 평가

MSBuild에는 MSBuild의 데이터를 스크립트 또는 도구에 쉽게 통합할 수 있는 새로운 기능이 포함되어 있습니다. dotnet publish와 같은 CLI 명령에 다음 새 플래그를 사용하여 CI 파이프라인 및 다른 곳에서 사용할 데이터를 가져올 수 있습니다.

 
Flag 설명
--getProperty:<PROPERTYNAME> 지정된 이름의 MSBuild 속성을 검색합니다.
--getItem:<ITEMTYPE> 지정된 형식의 MSBuild 항목을 검색합니다.
--getTargetResults:<TARGETNAME> 지정된 대상을 실행하여 출력을 검색합니다.

값은 표준 출력에 기록됩니다. 다음 예제와 같이 여러 값 또는 복합 값이 JSON으로 출력됩니다.

.NET CLI
>dotnet publish --getProperty:OutputPath
bin\Release\net8.0\
.NET CLI
>dotnet publish -p PublishProfile=DefaultContainer --getProperty:GeneratedContainerDigest --getProperty:GeneratedContainerConfiguration
{
  "Properties": {
    "GeneratedContainerDigest": "sha256:ef880a503bbabcb84bbb6a1aa9b41b36dc1ba08352e7cd91c0993646675174c4",
    "GeneratedContainerConfiguration": "{\u0022config\u0022:{\u0022ExposedPorts\u0022:{\u00228080/tcp\u0022:{}},\u0022Labels\u0022...}}"
  }
}
.NET CLI
>dotnet publish -p PublishProfile=DefaultContainer --getItem:ContainerImageTags
{
  "Items": {
    "ContainerImageTags": [
      {
        "Identity": "latest",
        ...
    ]
  }
}

터미널 빌드 출력

dotnet build 에는 보다 현대화된 빌드 출력을 생성하는 새로운 옵션이 있습니다. 이 터미널 로거 출력은 생성된 프로젝트와 오류를 그룹화하고, 다중 대상 프로젝트에 대한 다양한 대상 프레임워크를 더 잘 구분하며, 빌드가 수행하는 작업에 대한 실시간 정보를 제공합니다. 새 출력을 옵트인하려면 이 --tl 옵션을 사용합니다. 이 옵션에 대한 자세한 내용은 dotnet 빌드 옵션을 참조 하세요.

간소화된 출력 경로

.NET 8에는 빌드 출력에 대한 출력 경로 및 폴더 구조를 간소화하는 옵션이 도입되었습니다. 이전에는 .NET 앱이 다양한 빌드 아티팩트를 위한 깊고 복잡한 출력 경로 집합을 생성했습니다. 간소화된 새로운 출력 경로 구조는 모든 빌드 출력을 공통 위치로 수집하므로 도구를 쉽게 예측할 수 있습니다.

자세한 내용은 아티팩트 출력 레이아웃을 참조 하세요.

dotnet workload clean 명령

.NET 8에는 여러 .NET SDK 또는 Visual Studio 업데이트를 통해 남아 있을 수 있는 워크로드 팩을 클린 위한 새로운 명령이 도입되었습니다. 워크로드를 관리할 때 문제가 발생하는 경우 다시 시도하기 전에 알려진 상태로 안전하게 복원하는 것이 좋습니다 workload clean . 명령에는 두 가지 모드가 있습니다.

  • dotnet workload cleanVisual Studio가 설치된 경우 명령은 Visual Studio를 사용하여 수동으로 클린 워크로드도 나열합니다.
  • 분리된 팩을 클린 파일 기반 또는 MSI 기반 워크로드에 대한 워크로드 가비지 수집을 실행합니다. 분리된 팩은 제거된 버전의 .NET SDK 또는 팩에 대한 설치 레코드가 더 이상 존재하지 않는 팩에서 가져온 것입니다.
  • dotnet workload clean --all
  • 이 모드는 더 공격적이며 현재 SDK 워크로드 설치 유형인 컴퓨터의 모든 팩을 클린 있습니다(Visual Studio가 아님). 또한 실행 중인 .NET SDK 기능 밴드 및 아래에 대한 모든 워크로드 설치 레코드를 제거합니다.

dotnet publish 및 dotnet pack 자산

dotnet publishdotnet pack 명령은 프로덕션 자산을 생성하기 위한 것이므로 기본적으로 자산을 생성 Release 합니다.

다음 출력은 서로 다른 동작과 dotnet builddotnet publish속성을 false로 설정하여 게시 Debug 자산에 되돌리기 수 있는 PublishRelease 방법을 보여 줍니다.

콘솔
/app# dotnet new console
/app# dotnet build
  app -> /app/bin/Debug/net8.0/app.dll
/app# dotnet publish
  app -> /app/bin/Release/net8.0/app.dll
  app -> /app/bin/Release/net8.0/publish/
/app# dotnet publish -p:PublishRelease=false
  app -> /app/bin/Debug/net8.0/app.dll
  app -> /app/bin/Debug/net8.0/publish/

자세한 내용은 'dotnet pack'이 릴리스 구성을 사용하고 'dotnet publish'가 릴리스 구성을 사용하는 경우를 참조하세요.

dotnet restore 보안 감사

.NET 8부터 종속성 패키지가 복원될 때 알려진 취약성에 대한 보안 검사 옵트인할 수 있습니다. 이 감사는 영향을 받는 패키지 이름, 취약성의 심각도 및 자세한 내용을 위한 권고 링크가 포함된 보안 취약성에 대한 보고서를 생성합니다. 실행하거나dotnet restore, dotnet add 발견된 모든 취약성에 대해 NU1901-NU1904 경고가 표시됩니다. 자세한 내용은 보안 취약성에 대한 감사를 참조 하세요.

템플릿 엔진

템플릿 엔진NuGet의 보안 관련 기능 중 일부를 통합하여 .NET 8에서 보다 안전한 환경을 제공합니다. 다음과 같은 향상 된 기능

  • 기본적으로 피드에서 http:// 패키지 다운로드를 방지합니다. 예를 들어 다음 명령은 원본 URL이 HTTPS를 사용하지 않으므로 템플릿 패키지를 설치하지 못합니다.플래그를 사용하여 이 제한을 재정의할 --force 수 있습니다.
  • dotnet new install console --add-source "http://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public/nuget/v3/index.json"
  • dotnet newdotnet new installdotnet new update템플릿 패키지의 알려진 취약성에 대한 검사 . 취약성이 발견되고 계속 진행하려는 경우 플래그를 --force 사용해야 합니다.
  • 의 경우 dotnet new템플릿 패키지 소유자에 대한 정보를 제공합니다. 소유권은 NuGet 포털에서 확인되며 신뢰할 수 있는 특성으로 간주될 수 있습니다.
  • dotnet uninstall및 dotnet search , "신뢰할 수 있는" 패키지에서 템플릿이 설치되어 있는지 여부를 나타냅니다. 즉, 예약된 접두사를 사용합니다.

소스 링크 는 이제 .NET SDK에 포함됩니다. 목표는 원본 링크를 SDK에 번들로 묶어 패키지에 대해 별도로 <PackageReference> 요구하는 대신, 더 많은 패키지에 기본적으로 이 정보가 포함된다는 것입니다. 이 정보는 개발자를 위한 IDE 환경을 향상시킵니다.

원본 빌드 SDK

Linux 배포 빌드(원본 빌드) SDK에는 이제 원본 빌드 런타임 패키지를 사용하여 자체 포함 애플리케이션을 빌드하는 기능이 있습니다. 배포별 런타임 패키지는 원본 빌드 SDK와 함께 번들로 제공됩니다. 자체 포함 배포 중에 이 번들 런타임 패키지를 참조하여 사용자에게 기능을 사용하도록 설정합니다.

세계화

iOS/tvOS/MacCatalyst의 HybridGlobalization 모드

이제 모바일 앱은 더 가벼운 ICU 번들을 사용하는 새로운 하이브리드 세계화 모드를 사용할 수 있습니다. 하이브리드 모드에서 세계화 데이터는 부분적으로 ICU 번들에서 가져오고 부분적으로 네이티브 API 호출에서 가져옵니다. 모바일에서 지원하는 모든 로캘을 제공합니다.

HybridGlobalization 는 모드에서 InvariantGlobalization 작동할 수 없고 모바일의 ICU 데이터에서 트리밍된 문화권을 사용하는 앱에 가장 적합합니다. 더 작은 ICU 데이터 파일을 로드하려는 경우에도 사용할 수 있습니다. (icudt_hybrid.dat 파일은 기본 ICU 데이터 파일 icudt.dat보다 34.5% 작습니다.)

모드를 사용 HybridGlobalization 하려면 MSBuild 속성을 true로 설정합니다.

XML
<PropertyGroup>
  <HybridGlobalization>true</HybridGlobalization>
</PropertyGroup>

주의해야 할 몇 가지 제한 사항은 다음과 같습니다.

  • 네이티브 API의 제한 사항으로 인해 하이브리드 모드에서 모든 세계화 API가 지원되지는 않습니다.
  • 지원되는 API 중 일부는 다른 동작을 갖습니다.

애플리케이션이 영향을 받지 않도록 하려면 동작 차이점을 참조 하세요.

컨테이너

컨테이너 이미지

.NET 8용 .NET 컨테이너 이미지는 다음과 같이 변경되었습니다.

생성된 이미지 기본값

이제 Microsoft .NET 컨테이너의 새로운 non-root 기능이 기본값으로 적용되어 앱이 기본적으로 안전하게 유지됩니다. 사용자 고유 ContainerUser의 기본값을 설정하여 언제든지 이 기본값을 변경합니다.

기본 컨테이너 태그는 이제 latest. 이 기본값은 컨테이너 공간의 다른 도구와 일치하며 내부 개발 루프에서 컨테이너를 더 쉽게 사용할 수 있도록 합니다.

Debian 12

이제 컨테이너 이미지는 Debian 12(Bookworm)를 사용합니다. Debian은 .NET 컨테이너 이미지의 기본 Linux 배포판입니다.

루트가 아닌 사용자

이미지에는 non-root 사용자가 포함되었습니다. 이 사용자는 이미지를 non-root 지원합니다. 실행 non-root하려면 Dockerfile의 끝에 다음 줄을 추가합니다(또는 Kubernetes 매니페스트의 유사한 명령).

Dockerfile
USER app

.NET 8은 사용자에 대한 UID(64198)에 대한 non-root 환경 변수를 추가합니다. 이 환경 변수는 이름이 아닌 UID를 통해 컨테이너 사용자를 설정해야 하는 Kubernetes runAsNonRoot 테스트에 유용합니다. 이 dockerfile 은 사용 예제를 보여 줍니다.

기본 포트도 포트 80 에서 .로 8080변경되었습니다. 이 변경을 지원하기 위해 포트를 보다 쉽게 변경할 수 있도록 새 환경 변수 ASPNETCORE_HTTP_PORTS 를 사용할 수 있습니다. 변수는 포트 목록을 허용합니다. 이 목록은 필요한 형식 ASPNETCORE_URLS보다 간단합니다. 이러한 변수 중 하나를 사용하여 포트를 다시 포트 80 로 변경하는 경우 다음으로 non-root실행할 수 없습니다.

끌로 표시된 Ubuntu 이미지

Chiseled Ubuntu 이미지는 .NET 8에서 사용할 수 있습니다. 끌 이미지는 매우 작고 패키지 관리자 또는 셸이 없으며 공격된 표면이 감소합니다 non-root. 이 유형의 이미지는 어플라이언스 스타일 컴퓨팅의 이점을 원하는 개발자를 위한 것입니다. 깎은 이미지는 .NET 야간 아티팩트 레지스트리에 게시 됩니다.

다중 플랫폼 컨테이너 이미지 빌드

Docker는 여러 환경에서 작동하는 다중 플랫폼 이미지의 사용 및 빌드를 지원합니다. .NET 8에는 빌드한 .NET 이미지와 아키텍처를 혼합하고 일치시킬 수 있는 새로운 패턴이 도입되었습니다. 예를 들어 macOS를 사용하고 Azure에서 x64 클라우드 서비스를 대상으로 지정하려는 경우 다음과 같이 스위치를 사용하여 --platform 이미지를 빌드할 수 있습니다.

docker build --pull -t app --platform linux/amd64

이제 .NET SDK는 복원 시 값과 인수를 -a 지원합니다$TARGETARCH. 다음 코드 조각은 예제를 보여줍니다.

Dockerfile
RUN dotnet restore -a $TARGETARCH

# Copy everything else and build app.
COPY aspnetapp/. .
RUN dotnet publish -a $TARGETARCH --self-contained false --no-restore -o /app

자세한 내용은 다중 플랫폼 컨테이너 지원 개선 블로그 게시물을 참조하세요.

복합 이미지 ASP.NET

컨테이너화 성능을 개선하기 위한 노력의 일환으로, 런타임의 복합 버전이 있는 새로운 ASP.NET Docker 이미지를 사용할 수 있습니다. 이 복합은 여러 MSIL 어셈블리를 R2R(즉시 실행) 출력 이진 파일로 컴파일하여 빌드됩니다. 이러한 어셈블리는 단일 이미지에 포함되므로 지팅 시간이 줄어들고 앱의 시작 성능이 향상됩니다. 일반 ASP.NET 이미지에 비해 복합 이미지의 다른 큰 장점은 복합 이미지의 크기가 디스크에서 더 작다는 것입니다.

주의해야 할 사항이 있습니다. 복합에는 여러 어셈블리가 하나로 포함되어 있으므로 더 엄격한 버전 결합이 있습니다. 앱은 사용자 지정 버전의 프레임워크 또는 ASP.NET 이진 파일을 사용할 수 없습니다.

복합 이미지는 리포지토리에서 mcr.microsoft.com/dotnet/nightly/aspnet Alpine Linux, Jammy Chiseled 및 Mariner 배포판 플랫폼에서 사용할 수 있습니다. 태그는 ASP.NET Docker 페이지에 접미사와 함께 -composite 나열됩니다.

컨테이너 게시

성능 및 호환성

.NET 8은 컨테이너를 원격 레지스트리, 특히 Azure 레지스트리에 푸시하기 위한 성능이 향상되었습니다. 속도 향상은 계층을 한 번의 작업으로 푸시하는 것에서 비롯되며, 원자성 업로드를 지원하지 않는 레지스트리의 경우 보다 안정적인 청크 처리 메커니즘을 제공합니다.

또한 이러한 향상된 기능은 더 많은 레지스트리(Harbor, Artifactory, Quay.io 및 Podman)가 지원됨을 의미합니다.

인증

.NET 8은 레지스트리에 컨테이너를 푸시할 때 OAuth 토큰 교환 인증(Azure 관리 ID)에 대한 지원을 추가합니다. 이 지원은 이제 인증 오류 없이 Azure Container Registry와 같은 레지스트리로 푸시할 수 있음을 의미합니다. 다음 명령은 예제 게시 흐름을 보여 줍니다.

콘솔
> az acr login -n <your registry name>
> dotnet publish -r linux-x64 -p PublishProfile=DefaultContainer

.NET 앱을 컨테이너화하는 방법에 대한 자세한 내용은 dotnet 게시를 사용하여 .NET 앱 컨테이너화를 참조하세요.

tar.gz 보관에 게시

.NET 8부터 tar.gz 보관 파일로 직접 컨테이너를 만들 수 있습니다. 이 기능은 워크플로가 간단하지 않고 이미지를 푸시하기 전에 이미지에 대해 검사 도구를 실행해야 하는 경우에 유용합니다. 보관이 만들어지면 이동하거나, 스캔하거나, 로컬 Docker 도구 체인에 로드할 수 있습니다.

보관 파일에 게시하려면 명령에 속성을 추가 ContainerArchiveOutputPath 합니다 dotnet publish . 예를 들면 다음과 같습니다.

.NET CLI
dotnet publish \
  -p PublishProfile=DefaultContainer \
  -p ContainerArchiveOutputPath=./images/sdk-container-demo.tar.gz

폴더 이름 또는 특정 파일 이름을 가진 경로를 지정할 수 있습니다.

원본에서 생성된 COM interop

.NET 8에는 COM 인터페이스와의 상호 운용을 지원하는 새 원본 생성기가 포함되어 있습니다. 이 인터페이스를 GeneratedComInterfaceAttribute 사용하여 인터페이스를 원본 생성기의 COM 인터페이스로 표시할 수 있습니다. 소스 생성기는 C# 코드에서 관리되지 않는 코드로 호출할 수 있도록 하는 코드를 생성합니다. 또한 비관리 코드에서 C#으로 호출할 수 있도록 하는 코드를 생성합니다. 이 원본 생성기는 통합 LibraryImportAttribute되며 매개 변수로 형식을 GeneratedComInterfaceAttribute 사용하고 -attributed 메서드에서 LibraryImport형식을 반환할 수 있습니다.

C#
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.Marshalling;

[GeneratedComInterface]
[Guid("5401c312-ab23-4dd3-aa40-3cb4b3a4683e")]
interface IComInterface
{
    void DoWork();
}

internal class MyNativeLib
{
    [LibraryImport(nameof(MyNativeLib))]
    public static partial void GetComInterface(out IComInterface comInterface);
}

또한 소스 생성기는 특성을 사용하여 인터페이스를 구현하는 형식을 관리되지 않는 코드에 GeneratedComInterfaceAttribute 전달할 수 있도록 새 GeneratedComClassAttribute 특성을 지원합니다. 소스 생성기는 인터페이스를 구현하고 관리되는 구현에 호출을 전달하는 COM 개체를 노출하는 데 필요한 코드를 생성합니다.

특성이 있는 인터페이스의 GeneratedComInterfaceAttribute 메서드는 모두 동일한 형식LibraryImportAttribute을 지원하며LibraryImportAttribute, 이제는 -attributed 형식 및 GeneratedComClass-attributed 형식을 지원합니다GeneratedComInterface.

C# 코드에서만 -attributed 인터페이스를 사용하여 GeneratedComInterface관리되지 않는 코드에서 COM 개체를 래핑하거나 C#에서 관리되는 개체를 래핑하여 관리되지 않는 코드에 노출하는 경우 속성의 Options 옵션을 사용하여 생성될 코드를 사용자 지정할 수 있습니다. 이러한 옵션은 사용되지 않을 것으로 알고 있는 시나리오에 대해 마샬러를 작성할 필요가 없음을 의미합니다.

원본 생성기는 새 StrategyBasedComWrappers 형식을 사용하여 COM 개체 래퍼 및 관리되는 개체 래퍼를 만들고 관리합니다. 이 새로운 형식은 고급 사용자에 대한 사용자 지정 지점을 제공하면서 COM interop에 대한 예상 .NET 사용자 환경을 제공하는 것을 처리합니다. 애플리케이션에 COM에서 형식을 정의하는 고유한 메커니즘이 있거나 원본에서 생성된 COM이 현재 지원하지 않는 시나리오를 지원해야 하는 경우 새 StrategyBasedComWrappers 형식을 사용하여 시나리오에 누락된 기능을 추가하고 COM 형식에 대해 동일한 .NET 사용자 환경을 얻는 것이 좋습니다.

Visual Studio를 사용하는 경우 새 분석기 및 코드 수정을 통해 소스 생성 interop을 사용하도록 기존 COM interop 코드를 쉽게 변환할 수 있습니다. 전구는 ComImportAttribute각 인터페이스 옆에 소스 생성 interop으로 변환하는 옵션을 제공합니다. 이 수정은 특성을 사용하도록 인터페이스를 GeneratedComInterfaceAttribute 변경합니다. 그리고 인터페이스를 구현 GeneratedComInterfaceAttribute하는 모든 클래스 옆에 전구는 형식에 GeneratedComClassAttribute 특성을 추가하는 옵션을 제공합니다. 형식이 변환되면 메서드를 이동하여 DllImport 사용할 LibraryImportAttribute수 있습니다.

제한 사항

COM 원본 생성기는 키워드(keyword) 사용하여 new COM CoClass 및 다음 API를 활성화하는 아파트 선호도를 지원하지 않습니다.

  • IDispatch-based 인터페이스.
  • IInspectable-based 인터페이스.
  • COM 속성 및 이벤트입니다.

Linux의 .NET

Linux에 대한 최소 지원 기준

Linux에 대한 최소 지원 기준이 .NET 8에 대해 업데이트되었습니다. .NET은 모든 아키텍처에 대해 Ubuntu 16.04를 대상으로 빌드됩니다. 이는 .NET 8의 최소 glibc 버전을 정의하는 데 주로 중요합니다. .NET 8은 Ubuntu 14.04 또는 Red Hat Enterprise Linux 7과 같은 이전 glibc를 포함하는 배포판 버전에서 시작하지 못합니다.

자세한 내용은 Red Hat Enterprise Linux 제품군 지원을 참조하세요.

Linux에서 고유한 .NET 빌드

이전 .NET 버전에서는 원본에서 .NET을 빌드할 수 있었지만 릴리스에 해당하는 dotnet/installer 리포지토리 커밋에서 "원본 tarball"을 만들어야 했습니다. .NET 8에서는 더 이상 필요하지 않으며 dotnet/dotnet 리포지토리에서 직접 Linux에서 .NET을 빌드할 수 있습니다. 해당 리포지토리는 dotnet/source-build를 사용하여 .NET 런타임, 도구 및 SDK를 빌드 합니다. Red Hat 및 Canonical에서 .NET을 빌드하는 데 사용하는 것과 동일한 빌드입니다.

컨테이너 이미지에는 필요한 모든 종속성이 포함되어 있으므로 컨테이너에서 dotnet-buildtools/prereqs 빌드하는 것이 대부분의 사람들에게 가장 쉬운 방법입니다. 자세한 내용은 빌드 지침을 참조 하세요.

교차 빌드된 Windows 앱

Windows가 아닌 플랫폼에서 Windows를 대상으로 하는 앱을 빌드할 때 결과 실행 파일은 이제 지정된 Win32 리소스(예: 애플리케이션 아이콘, 매니페스트, 버전 정보)로 업데이트됩니다.

이전에는 이러한 리소스를 갖기 위해 Windows에서 애플리케이션을 빌드해야 했습니다. 인프라 복잡성과 리소스 사용량 모두에 영향을 미치는 중요한 문제이기 때문에 크로스 빌딩 지원에서 이러한 격차를 해결하는 것이 인기 있는 요청이었습니다.

Android 앱용 AOT 컴파일

앱 크기를 줄이기 위해 Android를 대상으로 하는 .NET 및 .NET MAUI 앱은 릴리스 모드에서 빌드될 때 프로파일된 AOT(미리 프로파일러) 컴파일 모드를 사용합니다. 프로파일된 AOT 컴파일은 일반 AOT 컴파일보다 더 적은 수의 메서드에 영향을 줍니다. .NET 8에는 <AndroidStripILAfterAOT> Android 앱에 대한 AOT 컴파일을 추가로 옵트인하여 앱 크기를 훨씬 더 줄일 수 있는 속성이 도입되었습니다.

XML
<PropertyGroup>
  <AndroidStripILAfterAOT>true</AndroidStripILAfterAOT>
</PropertyGroup>

기본적으로 기본 설정을 재정의 AndroidEnableProfiledAot 하도록 true 설정 AndroidStripILAfterAOT 하면 AOT 컴파일된 모든 메서드를 트리밍할 수 있습니다. 두 속성을 다음으로 명시적으로 설정하여 프로파일링된 AOT 및 IL 제거를 true함께 사용할 수도 있습니다.

XML
<PropertyGroup>
  <AndroidStripILAfterAOT>true</AndroidStripILAfterAOT>
  <AndroidEnableProfiledAot>true</AndroidEnableProfiledAot>
</PropertyGroup>

코드 분석

.NET 8에는 .NET 라이브러리 API를 정확하고 효율적으로 사용하고 있는지 확인하는 데 도움이 되는 몇 가지 새로운 코드 분석기 및 수정기가 포함되어 있습니다. 다음 표에서는 새 분석기를 요약합니다.

 
규칙 ID 범주 설명
CA1856 성능 매개 변수에 특성이 ConstantExpectedAttribute 올바르게 적용되지 않으면 발생합니다.
CA1857 성능 매개 변수에 주석이 추가 ConstantExpectedAttribute 되었지만 제공된 인수가 상수가 아닌 경우 발생합니다.
CA1858 성능 문자열이 지정된 접두사로 시작하는지 여부를 확인하려면 호출한 다음 결과를 0과 비교하는 것보다 호출 String.StartsWithString.IndexOf 하는 것이 좋습니다.
CA1859 성능 이 규칙은 가능하면 특정 지역 변수, 필드, 속성, 메서드 매개 변수 및 메서드 반환 형식의 형식을 인터페이스 또는 추상 형식에서 구체적인 형식으로 업그레이드하는 것이 좋습니다. 구체적인 형식을 사용하면 더 높은 품질의 생성된 코드가 생성됩니다.
CA1860 성능 컬렉션 형식에 요소가 있는지 여부를 확인하려면 사용하거나 호출하는 것보다 더 좋습니다LengthCount.Enumerable.AnyIsEmpty
CA1861 성능 인수로 전달된 상수 배열은 반복적으로 호출될 때 다시 사용되지 않으며, 이는 매번 새 배열이 생성됨을 의미합니다. 성능을 향상시키려면 배열을 정적 읽기 전용 필드로 추출하는 것이 좋습니다.
CA1865-CA1867 성능 char 오버로드는 단일 문자가 있는 문자열에 대해 성능이 뛰어난 오버로드입니다.
CA2021 안정성 Enumerable.Cast<TResult>(IEnumerable) 호환 Enumerable.OfType<TResult>(IEnumerable) 되는 형식이 올바르게 작동하려면 이 필요합니다. 확대 및 사용자 정의 변환은 제네릭 형식에서 지원되지 않습니다.
CA1510-CA1513 유지 관리 Throw 도우미는 새 예외 인스턴스를 if 생성하는 블록보다 더 간단하고 효율적입니다. 이러한 4개의 분석기는 다음과 같은 예외에 대해 생성되었습니다. ArgumentNullExceptionArgumentExceptionArgumentOutOfRangeExceptionObjectDisposedException

Windows Presentation Foundation

하드웨어 가속

이전에는 시스템에 하드웨어 렌더링 기능이 있더라도 원격으로 액세스된 모든 WPF 애플리케이션에서 소프트웨어 렌더링을 사용해야 했습니다. .NET 8은 RDP(원격 데스크톱 프로토콜)에 대한 하드웨어 가속을 옵트인할 수 있는 옵션을 추가합니다.

하드웨어 가속은 애플리케이션에서 그래픽 및 시각 효과의 렌더링 속도를 높이기 위해 컴퓨터의 GPU(그래픽 처리 장치)를 사용하는 것을 의미합니다. 이로 인해 성능이 향상되고 응답성이 뛰어난 그래픽이 향상될 수 있습니다. 반면 소프트웨어 렌더링은 컴퓨터의 CPU(중앙 처리 장치)에만 의존하여 그래픽을 렌더링하므로 속도가 느리고 효율성이 떨어집니다.

옵트인하려면 구성 속성을 true runtimeconfig.json 파일로 설정합니다Switch.System.Windows.Media.EnableHardwareAccelerationInRdp. 자세한 내용은 RDP의 하드웨어 가속을 참조 하세요.

OpenFolderDialog

WPF에는 .라는 새 대화 상자 컨트롤이 OpenFolderDialog포함되어 있습니다. 이 컨트롤을 사용하면 앱 사용자가 폴더를 찾아보고 선택할 수 있습니다. 이전에는 앱 개발자가 이 기능을 달성하기 위해 타사 소프트웨어에 의존했습니다.

C#
var openFolderDialog = new OpenFolderDialog()
{
    Title = "Select folder to open ...",
    InitialDirectory = Environment.GetFolderPath(
        Environment.SpecialFolder.ProgramFiles)
};

string folderName = "";
if (openFolderDialog.ShowDialog())
{
    folderName = openFolderDialog.FolderName;
}

자세한 내용은 .NET 8(.NET 블로그)의 WPF 파일 대화 상자 개선 사항을 참조하세요.

NuGet

.NET 8부터 NuGet은 기본적으로 Linux에서 서명된 패키지를 확인합니다. NuGet은 Windows에서도 서명된 패키지를 계속 확인합니다.

대부분의 사용자는 확인을 알 수 없습니다. 그러나 /etc/pki/ca-trust/extracted/pem/objsign-ca-bundle.pem에 있는 기존 루트 인증서 번들이 있는 경우 NU3042 경고와 함께 트러스트 오류가 표시될 수 있습니다.

환경 변수 DOTNET_NUGET_SIGNATURE_VERIFICATION 를 .로 설정하여 확인을 옵트아웃할 false수 있습니다.

진단

C# 핫 다시 로드 제네릭 수정을 지원합니다.

.NET 8부터 C# 핫 다시 로드 제네릭 형식 및 제네릭 메서드 수정을 지원합니다. Visual Studio를 사용하여 콘솔, 데스크톱, 모바일 또는 WebAssembly 애플리케이션을 디버그하는 경우 C# 코드 또는 Razor 페이지에서 제네릭 클래스 및 제네릭 메서드에 변경 내용을 적용할 수 있습니다. 자세한 내용은 Roslyn에서 지원하는 편집의 전체 목록을 참조하세요.

참고 항목

.NET 블로그

GitHub에서 Microsoft와 공동 작업
이 콘텐츠의 원본은 GitHub에서 찾을 수 있으며, 여기서 문제와 끌어오기 요청을 만들고 검토할 수도 있습니다. 자세한 내용은 참여자 가이드를 참조하세요.

.NET 피드백

.NET은(는) 오픈 소스 프로젝트입니다. 다음 링크를 선택하여 피드백을 제공해 주세요.

Posted by gurupia
,