我遇到了一种情况,我需要生成一个具有大字符串常量的类。我无法控制的代码会导致我生成的 CodeDom 树被发送到 C# 源代码,然后编译为更大的程序集的一部分。
不幸的是,我遇到了一种情况,如果该字符串的长度在 Win2K8 x64 中超过 335440 个字符(在 Win2K3 x86 中为 926240),C# 编译器将退出并出现致命错误:
致命错误 CS1647:表达式太长或太复杂,无法在“int”附近编译
MSDN 称 CS1647 是“编译器中的堆栈溢出”(没有双关语!)。更仔细地观察,我发现 CodeDom “很好地”将我的字符串 const 包装为 80 个字符。这导致编译器连接超过 4193 个字符串块,这显然是 x64 NetFx 中 C# 编译器的堆栈深度。 CSC.exe 必须在内部递归地计算此表达式以“补充”我的单个字符串。
我最初的问题是:“有谁知道改变代码生成器发出字符串的方式的解决方法?“我无法控制外部系统使用 C# 源代码作为中间体的事实,并且我希望它是一个常量(而不是运行时字符串连接)。
或者,我怎样才能制定这个表达式,以便在一定数量的字符之后,我仍然能够创建一个常量,但它由多个组成large chunks?
完整的重现在这里:
// this string breaks CSC: 335440 is Win2K8 x64 max, 926240 is Win2K3 x86 max
string HugeString = new String('X', 926300);
CodeDomProvider provider = CodeDomProvider.CreateProvider("C#");
CodeCompileUnit code = new CodeCompileUnit();
// namespace Foo {}
CodeNamespace ns = new CodeNamespace("Foo");
code.Namespaces.Add(ns);
// public class Bar {}
CodeTypeDeclaration type = new CodeTypeDeclaration();
type.IsClass = true;
type.Name = "Bar";
type.Attributes = MemberAttributes.Public;
ns.Types.Add(type);
// public const string HugeString = "XXXX...";
CodeMemberField field = new CodeMemberField();
field.Name = "HugeString";
field.Type = new CodeTypeReference(typeof(String));
field.Attributes = MemberAttributes.Public|MemberAttributes.Const;
field.InitExpression = new CodePrimitiveExpression(HugeString);
type.Members.Add(field);
// generate class file
using (TextWriter writer = File.CreateText("FooBar.cs"))
{
provider.GenerateCodeFromCompileUnit(code, writer, new CodeGeneratorOptions());
}
// compile class file
CompilerResults results = provider.CompileAssemblyFromFile(new CompilerParameters(), "FooBar.cs");
// output reults
foreach (string msg in results.Output)
{
Console.WriteLine(msg);
}
// output errors
foreach (CompilerError error in results.Errors)
{
Console.WriteLine(error);
}
使用 CodeSnippetExpression 和手动引用的字符串,我能够发出我希望从 Microsoft.CSharp.CSharpCodeGenerator 看到的源代码。
因此,要回答上面的问题,请替换这一行:
field.InitExpression = new CodePrimitiveExpression(HugeString);
有了这个:
field.InitExpression = new CodeSnippetExpression(QuoteSnippetStringCStyle(HugeString));
最后修改引用 Microsoft.CSharp.CSharpCodeGenerator.QuoteSnippetStringCStyle 方法的私有字符串,使其在 80 个字符后不换行:
private static string QuoteSnippetStringCStyle(string value)
{
// CS1647: An expression is too long or complex to compile near '...'
// happens if number of line wraps is too many (335440 is max for x64, 926240 is max for x86)
// CS1034: Compiler limit exceeded: Line cannot exceed 16777214 characters
// theoretically every character could be escaped unicode (6 chars), plus quotes, etc.
const int LineWrapWidth = (16777214/6) - 4;
StringBuilder b = new StringBuilder(value.Length+5);
b.Append("\r\n\"");
for (int i=0; i<value.Length; i++)
{
switch (value[i])
{
case '\u2028':
case '\u2029':
{
int ch = (int)value[i];
b.Append(@"\u");
b.Append(ch.ToString("X4", CultureInfo.InvariantCulture));
break;
}
case '\\':
{
b.Append(@"\\");
break;
}
case '\'':
{
b.Append(@"\'");
break;
}
case '\t':
{
b.Append(@"\t");
break;
}
case '\n':
{
b.Append(@"\n");
break;
}
case '\r':
{
b.Append(@"\r");
break;
}
case '"':
{
b.Append("\\\"");
break;
}
case '\0':
{
b.Append(@"\0");
break;
}
default:
{
b.Append(value[i]);
break;
}
}
if ((i > 0) && ((i % LineWrapWidth) == 0))
{
if ((Char.IsHighSurrogate(value[i]) && (i < (value.Length - 1))) && Char.IsLowSurrogate(value[i + 1]))
{
b.Append(value[++i]);
}
b.Append("\"+\r\n");
b.Append('"');
}
}
b.Append("\"");
return b.ToString();
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)