CA1838:避免 StringBuilder
P/Invokes 的參數
屬性 | 值 |
---|---|
規則識別碼 | CA1838 |
標題 | 避免 StringBuilder P/Invokes 的參數 |
類別 | 效能 |
修正程式是中斷或非中斷 | 不中斷 |
預設在 .NET 8 中啟用 | No |
原因
P/Invoke 具有 StringBuilder 參數。
檔案描述
StringBuilder
的封送處理一律會建立原生緩衝區複本,導致一個 P/Invoke 呼叫有多個配置。 若要封送處理 StringBuilder
為 P/Invoke 參數,運行時間會:
- 配置原生緩衝區。
In
如果是參數,請將 的內容StringBuilder
複製到原生緩衝區。Out
如果是參數,請將原生緩衝區複製到新配置的Managed陣列。
預設為 StringBuilder
In
與 Out
。
如需封送處理字串的詳細資訊,請參閱 字串的預設封送處理。
默認會停用此規則,因為可能需要逐一案例分析違規是否感興趣,而且可能不簡單重構以處理違規。 用戶可以藉由 設定其嚴重性來明確啟用此規則。
如何修正違規
一般而言,解決違規涉及重新處理 P/Invoke 及其呼叫者以使用緩衝區,而不是 StringBuilder
。 細節取決於 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
如需詳細資訊,請參閱 如何隱藏程式代碼分析警告。