You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

String.Replace抛出ArgumentOutOfRangeException:原因与复现方法探究

问题:String.Replace抛出ArgumentOutOfRangeException异常的原因及复现方法

代码示例

string QueryText = "unknown input"; // 这是一个属性

async Task MethodOneAsync() {
  // 此处为一些异步代码

  if (!string.IsNullOrEmpty(QueryText)) 
  {
    MethodTwo();
  }
}

void MethodTwo() {
  // 一些代码

  MethodThree();

  // 更多代码
}

void MethodThree() {
  string replacedText = QueryText.Replace(",", ""); // 此处抛出异常
}

异常信息

抛出异常:

System.ArgumentOutOfRangeException: Value must be positive. Parameter name: count

完整堆栈跟踪:

StringBuilder.Append (System.String value, System.Int32 startIndex, System.Int32 count)
String.ReplaceCore (System.String oldValue, System.String newValue, System.Globalization.CultureInfo culture, System.Globalization.CompareOptions options)
String.Replace (System.String oldValue, System.String newValue, System.StringComparison comparisonType)

构建与系统信息

  • 项目基于.NET Standard 2.1构建,用于Xamarin
  • 仅收到1份Android 13环境下的崩溃报告

已尝试的测试

编写测试函数:

static void Test(string text)
{
    text.Replace(",", "");
}

测试输入:

Test("");
Test(" ");
Test(null); // 抛出NullReferenceException

均未触发目标异常。

随后尝试多线程测试:

Task.Run(() => Test(" "));
Task.Run(() => Test(""));
Task.Run(() => Test("A"));
Task.Run(() => Test("B"));
Task.Run(() => Test("C"));
Task.Run(() => Test("D"));
Task.Run(() => Test("E"));
Task.Run(() => Test("F"));
Task.Run(() => Test("G"));
Task.Run(() => Test(","));
Task.Run(() => Test(" ,"));
Task.Run(() => Test(", "));
Task.Run(() => Test("a,"));
Task.Run(() => Test("a ,"));
Task.Run(() => Test("a, "));
Task.Run(() => Test(",a"));
Task.Run(() => Test(" ,a"));
Task.Run(() => Test(", a"));

依然未复现异常。


异常原因分析

1. 共享属性的并发修改竞态条件

QueryText作为实例属性,若在多线程环境下被并发修改(比如多个线程同时调用MethodOneAsync,或其他线程直接修改该属性),可能触发string.Replace内部实现的竞态问题。虽然string本身是不可变类型,但Xamarin的Mono运行时对Replace的实现可能依赖字符串的内部状态,当属性引用被异步切换时,会导致计算出的count参数非法。

2. Xamarin Android特定的字符串损坏场景

该异常仅出现在Android 13环境中,大概率和原生交互有关:如果QueryText的值来自Android原生API(如Intent、SharedPreferences、JNI调用),当原生层传递的字符串存在损坏(比如长度字段与实际字符数据不匹配),托管层的string实例内部状态会不一致。调用Replace时,内部StringBuilder.Append方法会根据字符串的Length计算参数,若实际可用字符数小于计算值,就会抛出ArgumentOutOfRangeException。

3. .NET Standard实现的偶发bug

Xamarin的Mono运行时对.NET Standard 2.1的string.Replace实现可能存在特殊场景下的bug,比如处理包含特殊Unicode字符、空字符或超长字符串时,会触发参数计算错误。


复现方法

1. 构造内部状态损坏的字符串

通过反射创建长度与实际字符数组不匹配的string实例,模拟原生层传递的损坏字符串:

using System.Reflection;

// 仅用于测试的不安全代码
char[] charArray = new char[] { 'x', ',', 'y' };
// 构造时传入的长度(5)远大于字符数组实际长度(3)
string corruptedString = (string)typeof(string)
    .GetConstructor(
        BindingFlags.NonPublic | BindingFlags.Instance,
        null,
        new Type[] { typeof(char[]), typeof(int), typeof(int) },
        null
    )
    .Invoke(new object[] { charArray, 0, 5 });

corruptedString.Replace(",", ""); // 会抛出目标异常

2. 结合Xamarin Android原生交互

在Android项目中,通过JNI手动构造长度不匹配的原生字符串,传递到托管层后赋值给QueryText,再调用MethodThree。

3. 高频并发修改+特殊输入

提升并发修改的频率,并加入特殊字符输入,模拟极端场景:

// 高频并发修改QueryText并执行替换操作
for (int i = 0; i < 2000; i++)
{
    Task.Run(() => 
    {
        // 随机生成包含大量逗号的字符串
        QueryText = new string(',', new Random().Next(0, 2000));
        MethodOneAsync().Wait();
    });
}

内容的提问来源于stack exchange,提问作者Tomáš Aresak Malčánek

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.09 05:09:51