다음을 통해 공유


UTF-8 문자열 리터럴

메모

이 문서는 기능 사양입니다. 사양은 기능의 디자인 문서 역할을 합니다. 여기에는 기능 디자인 및 개발 중에 필요한 정보와 함께 제안된 사양 변경 내용이 포함됩니다. 이러한 문서는 제안된 사양 변경이 완료되고 현재 ECMA 사양에 통합될 때까지 게시됩니다.

기능 사양과 완료된 구현 간에 약간의 불일치가 있을 수 있습니다. 이러한 차이는관련 LDM(언어 디자인 모임) 노트에서 캡처됩니다.

사양문서에서 C# 언어 표준에 기능 사양을 채택하는 과정에 대해 자세히 알아볼 수 있습니다.

요약

이 제안은 C#에서 UTF8 문자열 리터럴을 작성하고 UTF-8 byte 표현으로 자동으로 인코딩하는 기능을 추가합니다.

동기

UTF8은 웹의 언어이며 .NET 스택의 상당 부분에서 이 언어를 사용해야 합니다. 대부분의 데이터는 네트워크 스택에서 byte[] 형태로 제공되지만 코드에는 여전히 상수가 많이 사용됩니다. 예를 들어 네트워킹 스택은 일반적으로 "HTTP/1.0\r\n", " AUTH"과 같은 상수를 작성해야 합니다. "Content-Length: ".

현재는 C#이 UTF16 인코딩을 사용하는 모든 문자열을 나타내기 때문에 이 작업을 수행하는 데 효율적인 구문이 없습니다. 즉, 개발자는 런타임 시 인코딩 작업의 편리함과 그에 따른 오버헤드, 예를 들어 시작할 때 인코딩 작업을 실제로 수행하는 데 걸리는 시간과 실제로 필요하지 않은 형식에 대해 불필요하게 발생할 수 있는 할당 등의 부담을 감수할 것인지, 혹은 바이트를 수동으로 변환하여 byte[]에 저장할 것인지 선택해야 합니다.

// Efficient but verbose and error prone
static ReadOnlySpan<byte> AuthWithTrailingSpace => new byte[] { 0x41, 0x55, 0x54, 0x48, 0x20 };
WriteBytes(AuthWithTrailingSpace);

// Incurs allocation and startup costs performing an encoding that could have been done at compile-time
static readonly byte[] s_authWithTrailingSpace = Encoding.UTF8.GetBytes("AUTH ");
WriteBytes(s_authWithTrailingSpace);

// Simplest / most convenient but terribly inefficient
WriteBytes(Encoding.UTF8.GetBytes("AUTH "));

이러한 절충은 런타임, ASP.NET 및 Azure에서 파트너들이 자주 겪는 어려움입니다. byte[] 인코딩을 손으로 작성하는 번거로운 일을 피하고 싶어하기 때문에 종종 성능을 충분히 발휘하지 못합니다.

이 문제를 해결하기 위해 언어에 UTF8 리터럴을 허용하고, 컴파일 시간에 리터럴을 UTF8 byte[]으로 인코딩합니다.

상세 디자인

문자열 리터럴에는 u8 접미사가 붙는다.

언어는 문자열 리터럴에 u8 접미사를 제공하여 형식을 UTF8로 강제 적용합니다. 접미사는 대/소문자를 구분하지 않으며 U8 접미사가 지원되며 u8 접미사와 동일한 의미를 갖습니다.

u8 접미사를 사용하는 경우, 리터럴 값은 문자열의 UTF-8 바이트 표현을 포함하는 ReadOnlySpan<byte>입니다. 널 종결자는 호출 시 널 종료 문자열이 필요한 일부 interop 시나리오를 처리하기 위해 메모리의 마지막 바이트 및 ReadOnlySpan<byte>길이를 초과하여 배치되어 있습니다.

string s1 = "hello"u8;             // Error
var s2 = "hello"u8;                // Okay and type is ReadOnlySpan<byte>
ReadOnlySpan<byte> s3 = "hello"u8; // Okay.
byte[] s4 = "hello"u8;             // Error - Cannot implicitly convert type 'System.ReadOnlySpan<byte>' to 'byte[]'.
byte[] s5 = "hello"u8.ToArray();   // Okay.
Span<byte> s6 = "hello"u8;         // Error - Cannot implicitly convert type 'System.ReadOnlySpan<byte>' to 'System.Span<byte>'.

리터럴은 전역 상수로 할당되므로 결과 ReadOnlySpan<byte> 수명이 반환되거나 다른 곳으로 전달되는 것을 방지할 수 없습니다. 그러나 특히 비동기 함수 내의 특정 컨텍스트에서는 ref 구조체 형식의 로컬을 허용하지 않기 때문에, 이런 상황에서는 ToArray() 호출 또는 이와 유사한 호출이 필요하게 되어 사용상의 불이익이 발생할 수 있습니다.

u8 리터럴에는 상수 값이 없습니다. ReadOnlySpan<byte>는 오늘날 상수의 형식이 될 수 없기 때문입니다. const고려하기 위해 나중에 ReadOnlySpan<byte> 정의가 확장되면 이 값도 상수로 간주되어야 합니다. 사실상 이는 u8 리터럴을 선택적 매개 변수의 기본값으로 사용할 수 없음을 의미합니다.

// Error: The argument is not constant
void Write(ReadOnlySpan<byte> message = "missing"u8) { ... } 

리터럴의 입력 텍스트가 잘못된 형식의 UTF16 문자열이면 언어에서 오류가 발생합니다.

var bytes = "hello \uD8\uD8"u8; // Error: malformed UTF16 input string

var bytes2 = "hello \uD801\uD802"u8; // Allowed: invalid UTF16 values, but it's correctly formed.

더하기 연산자

새 글머리 기호가 §12.10.5 더하기 연산자에 다음과 같이 추가될 것입니다.

  • UTF-8 바이트 표현 결합:

    ReadOnlySpan<byte> operator +(ReadOnlySpan<byte> x, ReadOnlySpan<byte> y);
    

    이 이진 + 연산자는 바이트 시퀀스 연결을 수행하며 두 피연산자가 의미상 UTF8 바이트 표현인 경우에만 적용됩니다. 피연산자는 의미상 u8 리터럴의 값이거나 UTF8 바이트 표현 연결 연산자가 생성한 값인 경우 UTF8 바이트 표현입니다.

    UTF8 바이트 표현 연결의 결과는 왼쪽 피연산자의 바이트가 먼저 오고, 그 다음 오른쪽 피연산자의 바이트가 이어져 있는 ReadOnlySpan<byte>로 구성됩니다. 널 종결자는 호출 시 널 종료 문자열이 필요한 일부 interop 시나리오를 처리하기 위해 메모리의 마지막 바이트 및 ReadOnlySpan<byte>길이를 초과하여 배치되어 있습니다.

낮추기

언어는 개발자가 결과 byte[] 리터럴을 코드에 입력한 것처럼 UTF8로 인코딩된 문자열을 낮춥니다. 예를 들어:

ReadOnlySpan<byte> span = "hello"u8;

// Equivalent to

ReadOnlySpan<byte> span = new ReadOnlySpan<byte>(new byte[] { 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x00 }).
                               Slice(0,5); // The `Slice` call will be optimized away by the compiler.

즉, new byte[] { ... } 양식에 적용되는 모든 최적화가 utf8 리터럴에도 적용됩니다. 즉, C#에서 PE 파일의 .data 섹션에 저장하도록 최적화하기 때문에 호출 지점에서 메모리 할당이 발생하지 않습니다.

UTF8 바이트 표현 연결 연산자의 여러 연속적인 사용은 최종 바이트 시퀀스를 포함하는 바이트 배열을 사용하여 ReadOnlySpan<byte>의 단일 생성으로 축소됩니다.

ReadOnlySpan<byte> span = "h"u8 + "el"u8 + "lo"u8;

// Equivalent to

ReadOnlySpan<byte> span = new ReadOnlySpan<byte>(new byte[] { 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x00 }).
                               Slice(0,5); // The `Slice` call will be optimized away by the compiler.

단점

핵심 API 사용

컴파일러 구현은 잘못된 문자열을 탐지하고 UTF8Encoding로 변환하는 데 모두 byte[]을 사용할 것입니다. 정확한 API는 컴파일러가 사용하는 대상 프레임워크에 따라 달라질 수 있습니다. 그러나 UTF8Encoding 구현의 주력이 될 것입니다.

지금까지 컴파일러는 리터럴 처리를 위해 런타임 API를 사용하지 않았습니다. 이는 상수가 언어에서 런타임으로 처리되는 방식을 제어하기 때문입니다. 구체적으로 버그 수정과 같은 항목은 상수 인코딩을 변경할 수 있으며 C# 컴파일의 결과는 컴파일러가 실행 중인 런타임에 따라 달라집니다.

이것은 가상의 문제가 아닙니다. Roslyn의 초기 버전은 double.Parse 사용하여 부동 소수점 상수 구문 분석을 처리했습니다. 이로 인해 많은 문제가 발생했습니다. 우선, 일부 부동 소수점 값이 네이티브 컴파일러와 Roslyn 사이에서 다르게 표현되었습니다. 두 번째로 .NET Core가 진화하고 double.Parse 코드에서 오랜 버그를 수정함에 따라 컴파일러가 실행한 런타임에 따라 해당 상수의 의미가 언어로 변경되었습니다. 결과적으로 컴파일러는 자체 버전의 부동 소수점 구문 분석 코드를 작성하고 double.Parse대한 종속성을 제거했습니다.

이 시나리오는 런타임 팀과 논의되었으며 이전에 겪은 것과 동일한 문제가 있다고 생각하지 않습니다. UTF8 구문 분석이 런타임에서 안정적이며 이 영역에는 향후 호환 문제가 발생할 수 있는 알려진 문제가 없습니다. 만약 그런 일이 생긴다면, 전략을 다시 평가할 수 있습니다.

대안

대상 유형만

디자인은 대상 입력만 사용하고 u8 리터럴에서 string 접미사를 제거할 수 있습니다. 오늘날 대부분의 경우에 string 리터럴이 ReadOnlySpan<byte>에 직접 할당되고 있어서 불필요합니다.

ReadOnlySpan<byte> span = "Hello World;" 

u8 접미사는 주로 var 및 오버로드 해석이라는 두 가지 시나리오를 지원하기 위해 존재합니다. 후자의 경우 다음 사용 사례를 고려합니다.

void Write(ReadOnlySpan<byte> span) { ... } 
void Write(string s) {
    var bytes = Encoding.UTF8.GetBytes(s);
    Write(bytes.AsSpan());
}

구현을 감안할 때 Write(ReadOnlySpan<byte>) 호출하는 것이 좋으며 u8 접미사를 사용하면 편리합니다. Write("hello"u8). 개발자가 어색한 캐스팅 Write((ReadOnlySpan<byte>)"hello")의지할 필요가 없습니다.

여전히 이 기능은 편리한 기능이며, 없어도 존재할 수 있고 나중에 추가해도 문제가 없습니다.

Utf8String 유형을 기다리다

.NET 에코시스템은 오늘날 사실상의 Utf8 문자열 형식으로 ReadOnlySpan<byte>을 표준화하고 있지만, 미래에는 런타임에서 실제 Utf8String 형식을 도입할 수도 있습니다.

우리는 가능한 변화에 직면하여 여기에 우리의 디자인을 평가하고 우리가 내린 결정을 후회할지 여부를 숙고해야합니다. Utf8String을 도입할 현실적인 확률을 고려해야 합니다. 하지만 우리가 허용 가능한 대안으로 ReadOnlySpan<byte>을 찾는 매일마다 그 확률은 줄어드는 것 같습니다.

문자열 리터럴과 ReadOnlySpan<byte>간의 타겟 형식 변환을 후회할 가능성은 낮아 보입니다. 지금 API에 utf8로 ReadOnlySpan<byte>을 포함해 사용하고 있으므로, Utf8String이 함께 제공되고 "더 나은" 형식일지라도 변환에는 여전히 가치가 있습니다. 언어는 Utf8String보다 ReadOnlySpan<byte>으로의 변환을 단순히 선호할 수 있습니다.

u8 접미사가 ReadOnlySpan<byte>대신 Utf8String을 가리키는 것을 더 후회할 가능성이 높은 것 같습니다. stackalloc int[]이 자연스러운 유형을 int*가 아닌 Span<int>로 갖고 있는 것을 후회하는 것과 비슷합니다. 이건 거래를 성사시키지 못할 정도는 아니고, 단지 불편할 뿐입니다.

string 상수와 byte 시퀀스 간의 변환

이 섹션의 변환은 구현되지 않았습니다. 이러한 변환은 여전히 활성 제안입니다.

이 언어는 텍스트가 동등한 UTF8 바이트 표현으로 변환되는 string 상수와 byte 시퀀스 간의 변환을 허용합니다. 특히 컴파일러는 상수에서 string, byte[]Span<byte>로의 암시적 변환을 허용하는 ReadOnlySpan<byte>을 허용합니다. 새 글머리 기호가 암시적 변환 §10.2 섹션에 추가됩니다. 이 변환은 §10.4표준 변환이 아닙니다.

byte[] array = "hello";             // new byte[] { 0x68, 0x65, 0x6c, 0x6c, 0x6f }
Span<byte> span = "dog";            // new byte[] { 0x64, 0x6f, 0x67 }
ReadOnlySpan<byte> span = "cat";    // new byte[] { 0x63, 0x61, 0x74 }

변환에 대한 입력 텍스트가 잘못된 형식의 UTF16 문자열이면 언어에서 오류가 발생합니다.

const string text = "hello \uD801\uD802";
byte[] bytes = text; // Error: the input string is not valid UTF16

이 기능의 주된 사용은 리터럴과 함께 할 것으로 예상되지만, 어떤 string 상수 값에서도 작동할 것입니다. string 상수에서 null 값을 포함한 변환도 지원됩니다. 변환 결과는 대상 형식의 default 값입니다.

const string data = "dog"
ReadOnlySpan<byte> span = data;     // new byte[] { 0x64, 0x6f, 0x67 }

+과 같은 문자열에 대한 상수 작업의 경우, 전체 작업이 아닌 개별 파트마다 결과를 연결하기보다는 최종 string 단계에서 UTF8로 인코딩이 발생합니다. 이 순서 지정은 변환 성공 여부에 영향을 미칠 수 있으므로 고려해야 합니다.

const string first = "\uD83D";  // high surrogate
const string second = "\uDE00"; // low surrogate
ReadOnlySpan<byte> span = first + second;

여기서 두 부분은 서로게이트 쌍의 불완전한 부분이기 때문에 그 자체로 유효하지 않습니다. 개별적으로는 UTF8로의 올바른 변환이 없지만, 함께 모여 UTF8로 변환할 수 있는 완전한 대리자 쌍을 형성합니다.

Linq 식 트리에서는 string_constant_to_UTF8_byte_representation_conversion가 허용되지 않습니다.

이러한 변환에 대한 입력은 상수이고 데이터는 컴파일 시간에 완전히 인코딩되지만, 언어에서 변환을 상수로 간주하지 않습니다 . 배열이 현재 일정하지 않기 때문입니다. 나중에 배열을 고려하기 위해 const 정의가 확장되면 이러한 변환도 고려해야 합니다. 이는 실제로 이러한 변환의 결과를 선택적 매개 변수의 기본값으로 사용할 수 없음을 의미합니다.

// Error: The argument is not constant
void Write(ReadOnlySpan<byte> message = "missing") { ... } 

일단 구현되면 문자열 리터럴은 언어에서 다른 리터럴들이 가지는 문제와 동일한 문제가 발생합니다. 이들 리터럴이 나타내는 형식은 사용 방법에 따라 달라집니다. C#은 다른 리터럴의 의미를 구분하는 리터럴 접미사를 제공합니다. 예를 들어 개발자는 3.14f 작성하여 값을 float 또는 1l 강제로 값을 long수 있습니다.

해결되지 않은 질문

처음 세 가지 디자인 질문은 문자열과 Span<byte> / ReadOnlySpan<byte> 변환과 관련이 있습니다. 구현되지 않았습니다.

(해결됨) string 상수와 null 값 및 byte 시퀀스 간 변환

이 변환이 지원되는지 여부와 지원되는 경우 수행 방법은 지정되지 않습니다.

제안:

string 상수에서 null 값이 byte[], Span<byte>ReadOnlySpan<byte>로 암시적으로 변환될 수 있도록 허용합니다. 변환 결과는 대상 형식의 default 값입니다.

해결 방법:

제안이 승인되었습니다 - https://github.com/dotnet/csharplang/blob/main/meetings/2022/LDM-2022-01-26.md#conversions-from-null-literals.

(해결됨) string_constant_to_UTF8_byte_representation_conversion 어디에 해당합니까?

string_constant_to_UTF8_byte_representation_conversion이 암시적 변환 §10.2 섹션의 자체 글머리 기호인지, 아니면 §10.2.11의 일부인지, 또는 다른 기존 암시적 변환 그룹에 속하는 것인가요?

제안:

암시적 변환에서 "암시적 보간된 문자열 변환"이나 "메서드 그룹 변환"과 유사하게 새로운 글머리 기호입니다 §10.2. "원본이 상수 식이더라도 결과가 상수 식이 아니기 때문에 '암시적 상수 식 변환'에 속하지 않는 것처럼 느껴집니다." 또한 "암시적 상수 식 변환"은 사용자 정의 변환과 관련된 사소한 동작 변경으로 이어질 가능성이 있는 §10.4.2"표준 암시적 변환"으로 간주됩니다.

해결 방법:

문자열 상수를 UTF-8 바이트로 변환하는 새로운 종류를 소개하겠습니다 - https://github.com/dotnet/csharplang/blob/main/meetings/2022/LDM-2022-01-26.md#conversion-kinds

(해결됨) string_constant_to_UTF8_byte_representation_conversion이 표준 변환인가요?

컴파일러는 "순수" 표준 변환(표준 변환은 사용자 정의 변환의 일부로 발생할 수 있는 미리 정의된 변환) 외에도 일부 미리 정의된 변환을 "다소" 표준으로 처리합니다. 예를 들어, 암시적으로 보간된 문자열 변환은 코드에서 대상 형식에 대한 명시적 캐스트가 있는 경우에 사용자 정의 변환의 일부로 발생할 수 있습니다. 표준 명시적 변환인 것처럼, 암시적 변환이지만 표준 암시적 또는 명시적 변환 집합에 명시적으로 포함되지 않습니다. 예를 들어:

class C
{
    static void Main()
    {
        C1 x = $"hello"; // error CS0266: Cannot implicitly convert type 'string' to 'C1'. An explicit conversion exists (are you missing a cast?)
        var y = (C1)$"dog"; // works
    }
}

class C1
{
    public static implicit operator C1(System.FormattableString x) => new C1();
}

제안:

새 변환은 표준 변환이 아닙니다. 이렇게 하면 사용자 정의 변환과 관련된 사소한 동작 변경을 방지할 수 있습니다. 예를 들어 암시적 튜플 리터럴 변환 등에서 사용자 정의 변환에 대해 신경 쓸 필요가 없습니다.

해결 방법:

현재 https://github.com/dotnet/csharplang/blob/main/meetings/2022/LDM-2022-01-26.md#implicit-standard-conversion는 표준 변환이 아닙니다.

(해결됨) 린큐 식 트리 변환

Linq 식 트리 변환의 컨텍스트에서 string_constant_to_UTF8_byte_representation_conversion를 허용해야 합니까? 우리는 지금 당장은 허용하지 않거나 "낮춘" 형태를 트리에 포함시킬 수 있습니다. 예를 들어:

Expression<Func<byte[]>> x = () => "hello";           // () => new [] {104, 101, 108, 108, 111}
Expression<FuncSpanOfByte> y = () => "dog";           // () => new Span`1(new [] {100, 111, 103}) 
Expression<FuncReadOnlySpanOfByte> z = () => "cat";   // () => new ReadOnlySpan`1(new [] {99, 97, 116})

u8 접미사가 있는 문자열 리터럴은 어떨까요? 바이트 배열 생성으로 표시할 수 있습니다.

Expression<Func<byte[]>> x = () => "hello"u8;           // () => new [] {104, 101, 108, 108, 111}

해결 방법:

Linq 표현식 트리에서는 허용되지 않음 - https://github.com/dotnet/csharplang/blob/main/meetings/2022/LDM-2022-01-26.md#expression-tree-representation.

(해결됨) u8 접미사가 있는 문자열 리터럴의 기본 유형입니다.

"상세한 디자인" 섹션은 다음과 같이 설명합니다: "하지만 자연 타입은 ReadOnlySpan<byte>이 될 것입니다." 동시에 "u8 접미사를 사용하는 경우에는 리터럴이 여전히 허용되는 형식인 byte[], Span<byte> 또는 ReadOnlySpan<byte>로 변환될 수 있습니다."

이 방법에는 다음과 같은 몇 가지 단점이 있습니다.

  • ReadOnlySpan<byte> 데스크톱 프레임워크에서는 사용할 수 없습니다.
  • ReadOnlySpan<byte>에서 byte[] 또는 Span<byte>로의 기존 변환은 존재하지 않습니다. 이를 지원하기 위해 리터럴을 대상 형식으로 처리해야 할 수 있습니다. 언어 규칙과 구현이 모두 더 복잡해집니다.

제안:

자연형 타입은 byte[]입니다. 모든 프레임워크에서 쉽게 사용할 수 있습니다. 그런데 실행 시, 우리는 원래 제안에서도 항상 바이트 배열을 만드는 것으로 시작할 것입니다. 또한 Span<byte>ReadOnlySpan<byte>변환을 지원하기 위해 특별한 변환 규칙이 필요하지 않습니다. byte[]에서 Span<byte>ReadOnlySpan<byte>로의 암시적 사용자 정의 변환이 이미 존재합니다. ReadOnlyMemory<byte>으로의 사용자 정의 암시적 변환도 있습니다(아래 "변환의 깊이" 질문 참조). 사용자 정의 변환을 연결할 수 없다는 언어적인 단점이 있습니다. 따라서 다음 코드는 컴파일되지 않습니다.

using System;
class C
{
    static void Main()
    {
        var y = (C2)"dog"u8; // error CS0030: Cannot convert type 'byte[]' to 'C2'
        var z = (C3)"cat"u8; // error CS0030: Cannot convert type 'byte[]' to 'C3'
    }
}

class C2
{
    public static implicit operator C2(Span<byte> x) => new C2();
}

class C3
{
    public static explicit operator C3(ReadOnlySpan<byte> x) => new C3();
}

그러나 사용자 정의 변환과 마찬가지로 명시적 캐스트를 사용하여 한 사용자 정의 변환을 다른 사용자 정의 변환의 일부로 만들 수 있습니다.

모든 동기 부여 시나리오는 자연 타입인 byte[]를 사용해서 해결될 것 같은 느낌이 든다. 하지만 언어의 규칙과 구현은 훨씬 간단할 것이다.

해결 방법:

제안이 승인되었습니다 - https://github.com/dotnet/csharplang/blob/main/meetings/2022/LDM-2022-01-26.md#natural-type-of-u8-literals. u8 문자열 리터럴에 변경 가능한 배열 형식이 있어야 하는지에 대해 더 심층적인 토론을 하고 싶지만 지금은 논쟁이 필요하다고 생각하지 않습니다.

명시적 변환 연산자만 구현되었습니다.

(해결됨) 변환의 깊이

바이트[]가 작동할 수 있는 모든 곳에서도 작동합니까? 고려하십시오

static readonly ReadOnlyMemory<byte> s_data1 = "Data"u8;
static readonly ReadOnlyMemory<byte> s_data2 = "Data";

첫 번째 예는 u8에서 오는 자연스러운 특성 때문에 작동할 가능성이 큽니다.

두 번째 예제는 양방향으로 변환해야 하므로 작업을 하기 어렵습니다. 이는 ReadOnlyMemory<byte> 허용된 변환 형식 중 하나로 추가하지 않는 한입니다.

제안:

특별한 일은 하지 마십시오.

해결 방법:

현재 https://github.com/dotnet/csharplang/blob/main/meetings/2022/LDM-2022-01-26.md#conversion-depth새 변환 대상이 추가되지 않았습니다. 두 변환 모두 컴파일하지 않습니다.

(해결됨) 오버로드 해결 문제 발생

다음 API는 모호해집니다.

M("");
static void M1(ReadOnlySpan<char> charArray) => ...;
static void M1(byte[] byteArray) => ...;

이 문제를 해결하려면 어떻게 해야 할까요?

제안:

https://github.com/dotnet/csharplang/blob/main/proposals/csharp-10.0/lambda-improvements.md#overload-resolution과 마찬가지로, 변환 중 어떤 것도 상수를 UTF8 string 시퀀스로 변환할 필요가 없는 멤버를 선호하도록 더 나은 함수 멤버(byte)가 업데이트됩니다.

향상된 함수 멤버

... A 인수 식 집합과 매개 변수 형식 {E1, E2, ..., En}Mp를 가진 Mq{P1, P2, ..., Pn} 두 개의 적용 가능한 함수 멤버가 있는 인수 목록 {Q1, Q2, ..., Qn}이 주어졌을 때, Mp보다 Mq으로 정의됩니다.

  1. 각 인수에 대해 ExPx 암시적 변환은 string_constant_to_UTF8_byte_representation_conversion아니며 하나 이상의 인수에 대해 ExQx 암시적 변환은 string_constant_to_UTF8_byte_representation_conversion또는
  2. 각 인수에 대해 ExPx 암시적 변환은 function_type_conversion아니며,
    • Mp 제네릭이 아닌 메서드이거나 Mp 형식 매개 변수가 {X1, X2, ..., Xp} 있는 제네릭 메서드이며, 각 형식 매개 변수에 대해 형식 인수가 식에서 유추되거나 Xi이외의 형식에서 유추될 있습니다.
    • 하나 이상의 인수에 대해, Ex에서 Qx로의 암시적 변환이 function_type_conversion이거나, Mq가 형식 매개 변수 {Y1, Y2, ..., Yq}를 가진 제네릭 메서드인 경우, 하나 이상의 형식 매개 변수 Yi에 대해 형식 인수가 function_type으로부터 유추되거나,
  3. 각 인수에 대해 ExQx 암시적 변환은 ExPx암시적 변환보다 낫지 않으며 하나 이상의 인수에 대해 ExPx 변환이 ExQx변환보다 낫습니다.

이 규칙을 추가해도 인스턴스 메서드가 적용 가능해지고 확장 메서드가 "섀도링"되는 시나리오는 다루지 않습니다. 예를 들어:

using System;

class Program
{
    static void Main()
    {
        var p = new Program();
        Console.WriteLine(p.M(""));
    }

    public string M(byte[] b) => "byte[]";
}

static class E
{
    public static string M(this object o, string s) => "string";
}

이 코드의 동작은 자동으로 "string" 인쇄에서 "byte[]" 인쇄로 변경됩니다.

이 동작을 변경해도 괜찮습니까? 호환성이 손상되는 변경으로 문서화되어야 하나요?

C#10 언어 버전이 타겟으로 지정된 경우에도 string_constant_to_UTF8_byte_representation_conversion를 사용할 수 없게 하려는 제안은 없습니다. 이 경우 위의 예제는 C#10 동작으로 반환되는 대신 오류가 됩니다. 이는 대상 언어 버전이 언어의 의미 체계에 영향을 주지 않는다는 일반적인 원칙을 따릅니다.

이 행동이 괜찮습니까? 호환성이 손상되는 변경으로 문서화되어야 하나요?

또한 새 규칙은 튜플 리터럴 변환과 관련된 중단 문제를 방지하지 않습니다. 예를 들어

class C
{
    static void Main()
    {
        System.Console.Write(Test(("s", 1)));
    }

    static string Test((object, int) a) => "object";
    static string Test((byte[], int) a) => "array";
}

"array"를 "object" 대신 조용히 출력할 것입니다.

이 행동이 괜찮습니까? 호환성이 손상되는 변경으로 문서화되어야 하나요? 아마도 우리는 새 규칙을 더욱 복잡하게 만들어 튜플 리터럴 변환을 깊이 있게 탐구할 수 있을 것입니다.

해결 방법:

프로토타입은 여기에 규칙을 조정하지 않을 것입니다, 희망컨대 실제로 어떤 문제가 발생하는지 확인할 수 있습니다 - https://github.com/dotnet/csharplang/blob/main/meetings/2022/LDM-2022-01-26.md#breaking-changes.

(해결됨) u8 접미사가 대소문자를 구분하지 않아야 하나요?

제안:

숫자 접미사와의 일관성을 위해 U8 접미사를 지원합니다.

해결 방법:

승인됨 - https://github.com/dotnet/csharplang/blob/main/meetings/2022/LDM-2022-01-26.md#suffix-case-sensitivity.

오늘 예제

런타임이 현재 UTF8 바이트를 수동으로 인코딩한 경우의 예

우리가 성능을 충분히 활용하지 않는 예제들

디자인 회의

https://github.com/dotnet/csharplang/blob/main/meetings/2022/LDM-2022-01-26.md https://github.com/dotnet/csharplang/blob/main/meetings/2022/LDM-2022-04-18.md https://github.com/dotnet/csharplang/blob/main/meetings/2022/LDM-2022-06-06.md