CA1838: P/Invokes에 StringBuilder
매개 변수를 사용하지 마세요
속성 | 값 |
---|---|
규칙 ID | CA1838 |
제목 | P/Invokes에 대한 매개 변수 방지 StringBuilder |
범주 | 성능 |
수정 사항이 주요 변경인지 여부 | 주요 변경 아님 |
.NET 9에서 기본적으로 사용 | 아니요 |
원인
P/Invoke에 StringBuilder 매개 변수가 있습니다.
규칙 설명
마샬링은 StringBuilder
항상 네이티브 버퍼 복사본을 만들어 하나의 P/Invoke 호출에 대해 여러 할당을 생성합니다. StringBuilder
를 P/Invoke 매개 변수로 마샬링하기 위해 런타임은 다음을 수행합니다.
- 네이티브 버퍼를 할당합니다.
In
매개 변수인 경우StringBuilder
의 내용을 네이티브 버퍼에 복사합니다.Out
매개 변수인 경우 네이티브 버퍼를 새로 할당된 관리형 배열에 복사합니다.
기본적으로 StringBuilder
는 In
및 Out
입니다.
문자열 마샬링에 대한 자세한 내용은 문자열에 대한 기본 마샬링을 참조 하세요.
이 규칙은 위반이 중요한지와 위반을 해결하기 위한 어려운 리팩터링인지 여부에 대한 사례별 분석이 필요할 수 있기 때문에 기본적으로 사용하지 않도록 설정되어 있습니다. 사용자는 심각도를 구성하여 이 규칙을 사용하도록 명시적으로 설정할 수 있습니다.
위반 문제를 해결하는 방법
일반적으로 위반 문제 해결에는 StringBuilder
대신 버퍼를 사용하도록 P/Invoke 및 해당 호출자를 재작업하는 과정이 포함됩니다. 세부 사항은 P/Invoke에 대한 사용 사례에 따라 달라집니다.
다음은 네이티브 함수에서 채울 출력 버퍼로 StringBuilder
를 사용하는 일반적인 시나리오 예제입니다.
// Violation
[DllImport("MyLibrary", CharSet = CharSet.Unicode)]
private static extern void Foo(StringBuilder sb, ref int length);
public void Bar()
{
int BufferSize = ...
StringBuilder sb = new StringBuilder(BufferSize);
int len = sb.Capacity;
Foo(sb, ref len);
string result = sb.ToString();
}
버퍼가 작고 unsafe
코드가 허용되는 사용 사례에서는 stackalloc를 사용하여 스택에서 버퍼를 할당할 수 있습니다
[DllImport("MyLibrary", CharSet = CharSet.Unicode)]
private static extern unsafe void Foo(char* buffer, ref int length);
public void Bar()
{
int BufferSize = ...
unsafe
{
char* buffer = stackalloc char[BufferSize];
int len = BufferSize;
Foo(buffer, ref len);
string result = new string(buffer);
}
}
더 큰 버퍼에 대해서는 새 배열을 버퍼로 할당할 수 있습니다.
[DllImport("MyLibrary", CharSet = CharSet.Unicode)]
private static extern void Foo([Out] char[] buffer, ref int length);
public void Bar()
{
int BufferSize = ...
char[] buffer = new char[BufferSize];
int len = buffer.Length;
Foo(buffer, ref len);
string result = new string(buffer);
}
더 큰 버퍼에 대해 P/Invoke가 자주 호출되는 경우 ArrayPool<T>을 사용하여 반복되는 할당과 메모리 압력을 방지할 수 있습니다.
[DllImport("MyLibrary", CharSet = CharSet.Unicode)]
private static extern unsafe void Foo([Out] char[] buffer, ref int length);
public void Bar()
{
int BufferSize = ...
char[] buffer = ArrayPool<char>.Shared.Rent(BufferSize);
try
{
int len = buffer.Length;
Foo(buffer, ref len);
string result = new string(buffer);
}
finally
{
ArrayPool<char>.Shared.Return(buffer);
}
}
런타임까지 버퍼 크기를 알 수 없는 경우 stackalloc
을 사용하여 큰 버퍼를 할당하지 않으려면 크기에 따라 버퍼를 다르게 만들어야 할 수 있습니다.
위 예제에서는 2바이트 와이드 문자(CharSet.Unicode
)를 사용합니다. 네이티브 함수에서 1바이트 문자(CharSet.Ansi
)를 사용하는 경우 byte
버퍼를 char
버퍼 대신 사용할 수 있습니다. 예시:
[DllImport("MyLibrary", CharSet = CharSet.Ansi)]
private static extern unsafe void Foo(byte* buffer, ref int length);
public void Bar()
{
int BufferSize = ...
unsafe
{
byte* buffer = stackalloc byte[BufferSize];
int len = BufferSize;
Foo(buffer, ref len);
string result = Marshal.PtrToStringAnsi((IntPtr)buffer);
}
}
매개 변수가 입력으로도 사용되는 경우 null 종결자가 명시적으로 추가된 문자열 데이터로 버퍼를 채워야 합니다.
경고를 표시하지 않는 경우
마샬링이 성능에 미치는 영향을 염려하지 않는 경우 이 규칙 위반을 StringBuilder
표시하지 않습니다.
경고 표시 안 함
단일 위반만 표시하지 않으려면 원본 파일에 전처리기 지시문을 추가하여 규칙을 사용하지 않도록 설정한 후 다시 사용하도록 설정합니다.
#pragma warning disable CA1838
// The code that's violating the rule is on this line.
#pragma warning restore CA1838
파일, 폴더 또는 프로젝트에 대한 규칙을 사용하지 않도록 설정하려면 구성 파일에서 심각도를 none
으로 설정합니다.
[*.{cs,vb}]
dotnet_diagnostic.CA1838.severity = none
자세한 내용은 방법: 코드 분석 경고 표시 안 함을 참조하세요.
참고 항목
.NET