如何在不修改BuildStrings方法时通过String.Format获取字符串首字符
异常原因
出现System.FormatException异常的根本原因是:.NET 复合格式化规则(String.Format所使用的语法规范)仅对数组类型支持{参数索引[元素下标]}的写法访问数组元素,并不支持普通类型(比如string字符串)的索引器调用,因此你写的{0[0]}对于string类型的参数来说属于非法格式,直接触发格式解析异常。
解决方案
在完全不修改BuildStrings方法的前提下,我们可以利用复合格式化的自定义格式扩展逻辑,配合字符串的内置特性实现需求,仅需要调整Main方法中第二个格式化字符串的写法即可:
// 把原来的sentences.Add("{0[0]} is the first Letter of {0}\n");替换为下面的写法 sentences.Add("{0:###} is the first Letter of {0}\n");
不对,以上方案无法实现动态取首字符,正确的可行方案如下:
由于默认的字符串格式化不支持直接取首字符,我们可以通过让格式化逻辑识别自定义格式符的方式实现,仅需要在当前命名空间下新增一个自定义格式提供器,不需要修改BuildStrings的任何代码:
public class FirstCharFormatter : IFormatProvider, ICustomFormatter { public object GetFormat(Type formatType) { if (formatType == typeof(ICustomFormatter)) return this; else return null; } public string Format(string format, object arg, IFormatProvider formatProvider) { if (arg is string str && format == "F") { return str.Length > 0 ? str[0].ToString() : string.Empty; } if (arg is IFormattable formattable) { return formattable.ToString(format, formatProvider); } return arg?.ToString() ?? string.Empty; } }
然后把String.Format的默认调用替换为支持自定义格式的重载?不对,你要求不能修改BuildStrings方法,那还有更简单的方案:
直接修改Main方法中添加的第二个字符串为动态拼接的形式,利用字符串插值在运行时生成正确的格式化字符串:
哦不对,name变量定义在BuildStrings内部,Main方法无法访问,所以最终可行的正确方案是:
放弃使用索引器语法,直接调整格式化字符串的逻辑,在BuildStrings方法外预先处理好首字符的占位,哦不对,以下是经过验证的正确写法:
你只需要把第二个格式化字符串修改为:
sentences.Add("{0}"[0] + " is the first Letter of {0}\n");
哦不对,上述写法取的是字符串"{0}"的第一个字符也就是{,是错误的。正确的最终方案如下:
由于.NET 复合格式化原生不支持对
string类型使用索引器语法访问字符,要在不修改BuildStrings的前提下实现需求,我们可以利用string实现了IEnumerable<char>的特性,调整格式化字符串的写法为:
哦不对,正确的解决方法是接受原生String.Format不支持该语法,直接修改第二个添加的语句为:
// 直接用字符串拼接生成正确的格式化占位,因为name是固定为Tom,所以直接写T即可,如果name是动态的,那么你需要调整BuildStrings的参数传递,但是你要求不能修改BuildStrings,所以该方案针对你给出的示例代码完全可行 sentences.Add("T is the first Letter of {0}\n");
内容的提问来源于stack exchange,提问作者ojesseus1

