17

C# コードに次のメソッドがあります。

/// <summary>
/// Removes the first (leftmost) occurence of a <paramref name="substring"/> from a <paramref name="string"/>.
/// </summary>
/// <param name="string">The string to remove the <paramref name="substring"/> from. Cannot be <c>null</c>.</param>
/// <param name="substring">The substring to look for and remove from the <paramref name="string"/>. Cannot be <c>null</c>.</param>
/// <returns>
/// The rest of the <paramref name="string"/>, after the first (leftmost) occurence of the <paramref name="substring"/> in it (if any) has been removed.
/// </returns>
/// <remarks>
/// <list type="bullet">
/// <item>If the <paramref name="substring"/> does not occur within the <paramref name="string"/>, the <paramref name="string"/> is returned intact.</item>
/// <item>If the <paramref name="substring"/> has exactly one occurence within the <paramref name="string"/>, that occurence is removed, and the rest of the <paramref name="string"/> is returned.</item>
/// <item>If the <paramref name="substring"/> has several occurences within the <paramref name="substring"/>, the first (leftmost) occurence is removed, and the rest of the <paramref name="string"/> is returned.</item>
/// </list>
/// </remarks>
/// <exception cref="ArgumentNullException">
/// The <paramref name="string"/> is <c>null</c>. -or- The <paramref name="substring"/> is <c>null</c>.
/// </exception>
public static string RemoveSubstring(string @string, string substring)
{
    if (@string == null)
        throw new ArgumentNullException("string");

    if (substring == null)
        throw new ArgumentNullException("substring");

    var index = @string.IndexOf(substring);
    return index == -1
        ? @string
        : @string.Substring(0, index) + @string.Substring(index + substring.Length);
}

実装は非常に単純で明白に見え、単体テストによる優れたカバレッジを備えています。私のマシン、ビルド サーバー、またはアクセスできるその他のマシン、またはほとんどの運用環境で予期しない結果が発生したことはありません。

次のスタック トレースを使用して、この方法でアプリケーション クラッシュを時折報告するリモート カスタマーが 1 人だけであることを除きます。

System.ArgumentOutOfRangeException: startIndex cannot be larger than length of string.  
Parameter name: startIndex   
   at System.String.InternalSubStringWithChecks(Int32 startIndex, Int32 length, Boolean fAlwaysCopy)  
   at System.String.Substring(Int32 startIndex)
   at MyNamespace.StringUtils.RemoveSubstring(String string, String substring)
   at ...

残念ながら、私はこの実稼働環境やそのデータ、または追加情報へのリモート アクセスを持っていません。何らかの理由で、現在、ログ システムやクラッシュ ダンプ コレクションを展開できません。

コードを見て、さまざまな引数の組み合わせを試してみると、この例外がどのように発生するか想像できません。

アイデアを教えてください。

4

2 に答える 2

10
RemoveSubstring("A", "A\uFFFD"); // throws ArgumentOutOfRangeException
RemoveSubstring("A", "A\u0640"); // throws ArgumentOutOfRangeException

.NET での文字列操作のための多くの関数は、デフォルトでカルチャ固有のものを含みます (通常、ビットごとの比較を渡すか切り替えるIndexOfことができるオーバーロードがあります)。個人的には、デフォルトの動作として選択されたものにはあまり満足していませんが、明示的な開発ガイドラインと FxCop ルールを設定することを除いて、それについて何もするのは遅すぎます.StringComparison.OrdinalStringComparer.Ordinal

ただし、カルチャ固有の操作がまさに必要な場合もあります。残念ながら、それらのセマンティクスはトリッキーで直観に反する可能性があり、通常想定される不変条件に違反する可能性があり、対処すべき多くのコーナー ケースがあります。カルチャに依存するロジックをアプリケーションに実装する担当の開発者は、この分野で十分な資格があり、自分が何をしているのかを常に正確に理解している必要があります。この分野のレビューとテストの基準を通常よりも高く設定することをお勧めします。

于 2013-07-21T04:12:52.743 に答える
-1

コメントの後、無効なインデックスで部分文字列が失敗しないことがわかりました。本当の問題は

@string.IndexOf(substring);

ピエール・リュック・ピノーが指摘したように

修正:

@string.IndexOf(substring, StringComparison.Ordinal);
于 2013-07-21T03:57:59.930 に答える