c#双精度字符数组或替代

c#双精度字符数组或替代,c#,memory,tostring,C#,Memory,Tostring,我有一个程序,它存储了一堆结构实例,其中包含许多double类型的成员。我经常会将这些文件转储到一个文件中,这是我使用字符串生成器所做的,例如: StringBuilder builder = new StringBuilder(256); builder.AppendFormat("{0};{1};{2};", x.A.ToString(), x.B.ToString(), x.C.ToString()); 其中“x”是my类型的实例,A、B、C是double类型的x的成员。我对每一

我有一个程序,它存储了一堆结构实例,其中包含许多double类型的成员。我经常会将这些文件转储到一个文件中,这是我使用字符串生成器所做的,例如:

 StringBuilder builder = new StringBuilder(256);

 builder.AppendFormat("{0};{1};{2};", x.A.ToString(), x.B.ToString(), x.C.ToString()); 
其中“x”是my类型的实例,A、B、C是double类型的x的成员。我对每一个调用ToString()以避免装箱。然而,这些对ToString的调用仍然会在我的应用程序上下文中分配大量内存,我希望减少这种情况。我的想法是拥有一个字符数组,并将每个成员直接写入其中,然后从该字符数组中创建一个字符串并将其转储到文件中。几个问题:

1) 我想做的事情听起来合理吗?有没有人知道有什么东西可以达到类似的效果

2) 是否已经内置了将双精度转换为字符数组的功能(我想这将达到某种参数化精度?)。理想情况下,我希望传入数组和一些索引,并让它开始写入

我之所以尝试这样做,是为了在我的应用程序运行时减少内存中的大峰值,因为我运行了许多实例,并且经常发现自己受到内存的限制

干杯
A

文件必须是某种文本格式吗

如果不是的话,到目前为止最有效的方法是使用BinaryWriter(和BinaryReader)读回它们


有关更多信息,请参阅。

如果可以直接写入文本文件,则可以使用steamwriter编写强类型结构。我还没有测试内存使用情况,但我认为它们应该是有效的

        using (var tw = new System.IO.StreamWriter("filename", true)) //(true to append to file)
        {
            tw.Write(x.A);
            tw.Write(';');
        }
如果需要stringbuilder,也可以使用以下命令调用强类型重载:

        builder.Append(x.A) //strongly typed as long as the field is a system type
            .Append(';')
            .Append(x.B)
            .Append(';'); 
当然,这两种方法在实现某种循环或委托时看起来都更好,但这与装箱逻辑无关


编辑其他答案中发布的自定义双重写入:

您应该直接写入文件流以降低内存利用率

using(var writer = new StreamWriter(...))
{
   writer.Write(x.A);
   writer.Write(";");
   writer.Write(x.B);
   writer.Write(";");
   writer.Write(x.C);
}

您确定可能是对
Double.ToString
的多次调用导致了内存问题吗?应在下一代0收集中收集每个字符串,.NET垃圾收集器在这方面非常有效

如果创建的字符串超过85K,它们将在大型对象堆上创建,这可能会增加应用程序所需的总内存,即使大型字符串只是暂时存在(大型对象堆碎片)

您可以使用性能监视器了解有关应用程序如何使用托管堆的更多信息。您已经使用了CLRProfiler,这是一个更高级的工具,所以您可能不会学到任何新的东西


StringBuilder
是在内存中构建字符串的合适类,但是如果您只在内存中构建字符串以便以后将其写入文件,则应使用
StreamWriter
直接写入文件

StringBuilder
必须扩展用于存储字符串的缓冲区,您可以通过提前设置
StringBuilder
的容量来避免额外的开销(您已经在示例代码中这样做了)

无论调用什么重载将
Double
格式化为
StringBuilder
调用最终都会导致调用
Double.ToString
StringBuilder.AppendFormat
直接格式化到缓冲区,而不分配额外的格式化字符串,因此在内存使用方面
StringBuilder.AppendFormat
StringBuilder.Append
一样好,这两个重载都会将格式化的
Double
字符串分配为格式化的一部分过程但是,
StringBuilder.AppendFormat
将框住
Double
,因为它接受
params对象[]
数组。使用接受双精度的
StringBuilder.Append
重载不会出现此问题


如果您确信
Double.ToString
是内存问题的根源,我相信您最好的选择是编写自己的浮点格式代码,可以将浮点数字直接写入
StringBuilder
。这项任务并不简单,但你可以从开源C库中获得灵感。

出于对如何进行的纯粹好奇,我忍不住尝试创建一个直接编写Double的场景。下面是结果。我还没有对它进行基准测试,但在我运行的(有限的)测试中,它确实按照预期工作

        double[] test = { 8.99999999, -4, 34.567, -234.2354, 2.34, 500.8 };
        using (var sw = new FileStream(@"c:\temp\test.txt", FileMode.Create))
        {
            using (var bw = new BinaryWriter(sw))
            {
                const byte semicol = 59, minus = 45, dec = 46, b0 = 48;

                Action<double> write = d =>
                {
                    if (d == 0)
                        bw.Write(b0);
                    else
                    {
                        if (d < 0)
                        {
                            bw.Write(minus);
                            d = -d;
                        }

                        double m = Math.Pow(10d, Math.Truncate(Math.Log10(d)));
                        while(true)
                        {
                            var r = ((decimal)(d / m) % 10); //decimal because of floating point errors
                            if (r == 0) break;
                            if (m == 0.1)
                                bw.Write(dec); //decimal point
                            bw.Write((byte)(48 + r));         
                            m /= 10d;
                        }
                    }

                    bw.Write(semicol);
                };

                foreach (var d in test)
                    write(d);
            }
        }
double[]test={8.9999999,-4,34.567,-234.2354,2.34500.8};
使用(var sw=new FileStream(@“c:\temp\test.txt”,FileMode.Create))
{
使用(var bw=新二进制编写器(sw))
{
常量字节semicol=59,减号=45,dec=46,b0=48;
动作写入=d=>
{
如果(d==0)
bw.Write(b0);
其他的
{
if(d<0)
{
写(减);
d=-d;
}
double m=Math.Pow(10d,Math.Truncate(Math.Log10(d));
while(true)
{
var r=((十进制)(d/m)%10);//由于浮点错误而导致十进制
如果(r==0)中断;
如果(m==0.1)
bw.Write(dec);//小数点
写((字节)(48+r));
m/=10d;
}
}
写入(半角);
};
foreach(测试中的var d)
写(d);
}
}

StringBuilder
对于si来说是过度杀伤力