为什么 c# 在创建任何文件流之前创建文件?

Why is c# creating a file before any file stream is created?

提问人:Artemis Venatrix 提问时间:2/4/2022 最后编辑:CharliefaceArtemis Venatrix 更新时间:2/4/2022 访问量:522

问:

背景:
我正在创建一个 avaloniaUI 项目,其中我有一个 json 配置,其中包含一个类,可以在初始化和编辑时对其进行序列化和反序列化。如果在初始化时找不到该文件,则该类会创建一个包含默认属性列表的新文件。这个类应该是代码中唯一可以做到这一点的部分。

问题:
由于某种原因,配置文件(如果尚不存在)是在 config 序列化程序类的构造函数调用之前创建的。这导致我和我的朋友认为该文件是在项目的其他地方创建的。但事实并非如此,因为我使用控制 f 工具梳理整个项目以查找任何文件流、创建命令或对配置路径的引用,并且我发现除了应该创建配置但从未运行的代码之外,没有其他能够生成文件。因此,发生的情况是在调用应该处理生成的代码之前,文件被创建为空,从而跳过正确的生成代码,这会导致 json 在文件反序列化时加载空字符串,并创建空值异常。

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using Newtonsoft.Json;

namespace RefOrganizerAvalonia.Util
{
    public class ConfigSerializer
    {
        public List<ConfigItem> Items { get; }
        
        private string _configPath;
        
        private static List<ConfigItem> BuildConfig()
        {
            return new List<ConfigItem>()
            {
                new ConfigItem("PageResultSize", 50, typeof(int))
                ,new ConfigItem("ResultsPerLine", 5, typeof(int))
            };
        }

        private List<ConfigItem> LoadConfig()
        {
            using (FileStream fs = new FileStream(_configPath, FileMode.Open))
            {
                var sr = new StreamReader(fs);
                var dataStr = sr.ReadToEnd();
                return JsonConvert.DeserializeObject(dataStr) as List<ConfigItem>;
            }
        }

        public ConfigSerializer()
        { //break point here reveals that even before any of the constructor code is executed the 
          //file is created from an unknown location
            var folder = Environment.SpecialFolder.LocalApplicationData;
            var path = Environment.GetFolderPath(folder);
            _configPath = Path.Join(path, "ref_organizer_config.json");
            Debug.WriteLine(_configPath);
            Debug.WriteLine(File.Exists(_configPath));
            if (!File.Exists(_configPath)) //never accessed because the (empty) file already 
                                           //exists by this point despite no code that can create 
                                           //it being executed
            {
                Debug.WriteLine("Config Not Found");
                //Save BuildConfig() results in json string and write to file
                Items = BuildConfig();
                SaveConfig();
            }
            else
            {
                Items = LoadConfig();
            }
        }

        private void SaveConfig()
        {
            using (FileStream fs = new FileStream(_configPath, FileMode.OpenOrCreate))
            {
                Debug.WriteLine("Save Config Triggered");
                var data = JsonConvert.SerializeObject(Items);
                Debug.WriteLine($"Serialized Config: {data}");
                var sw = new StreamWriter(fs);
                sw.Write(data);
            }
        }
    }
}

执行的测试:

  1. 更改配置序列化程序类的调用顺序和位置。

    结果:在初始化配置序列化程序之前,仍会创建具有配置名称和路径的空文件。

  2. 更改变量的路径。_configPath

    结果:以新名称创建的空文件。

  3. 将功能设置为私有。SaveConfig()

    结果:没有变化。

注意:

如果您出于任何原因需要 avalonia 项目代码的其他上下文,请给我留言,我会尽快发布。

C# 文件 FileStream Streamwriter using-statement

评论

3赞 Andrew Morton 2/4/2022
如果注释掉应该处理生成的代码,然后重新生成项目,它是否仍会创建文件?
0赞 Jon Skeet 2/4/2022
我们真正能做的是演示问题的最小示例的代码。从项目中删减大量代码,在每次更改后重新测试,直到找到问题,或者可以向我们提供演示问题的代码。
0赞 Artemis Venatrix 2/4/2022
@AndrewMorton我刚刚根据您的建议对其进行了测试,不,当生成代码从文件中剪切出来时,它不会生成文件。
0赞 Andrew Morton 2/4/2022
@ArtemisVenatrix 因此,您需要在预期之前找出调用该代码的原因。如何找到调用当前方法的方法?
0赞 Artemis Venatrix 2/4/2022
@AndrewMorton 问题是由于某种原因,我的调试读数没有显示对该函数的第一次调用。我想知道它是否在调试初始化之前被调用。

答:

1赞 JohnG 2/4/2022 #1

在我跟踪代码的小型测试中,问题似乎出在方法上。遍历代码应该会发现代码行......正在执行,但是,实际上没有向文件写入任何内容。添加语句应确保... ...完成。将命令添加到编写器似乎实际上是将数据写入文件。SaveConfig()sw.Write(data);FlushStreamWritersw.Write(data);Flush()

private void SaveConfig() {
  using (FileStream fs = new FileStream(_configPath, FileMode.OpenOrCreate)) {
    Debug.WriteLine("Save Config Triggered");
    var data = JsonConvert.SerializeObject(Items);
    Debug.WriteLine($"Serialized Config: {data}");
    var sw = new StreamWriter(fs);
    sw.Write(data);
    sw.Flush();
  }
}

编辑。。。StreamWriter 应包装在 using 语句中。

进一步的测试表明,将语句包装在 周围是必需的,并且应该实现。因此,将 包装在语句中应该会为您提供数据。以下是该语句的更好方法。UsingStreamWriterStreamWriterusingFlushusing

private void SaveConfig() {
  using (FileStream fs = new FileStream(_configPath, FileMode.OpenOrCreate)) {
    Debug.WriteLine("Save Config Triggered");
    var data = JsonConvert.SerializeObject(Items);
    Debug.WriteLine($"Serialized Config: {data}");
    using (var sw = new StreamWriter(fs)) {
      sw.Write(data);
    }
  }
}

评论

0赞 Artemis Venatrix 2/4/2022
谢谢你解决了!
0赞 Andrew Morton 2/5/2022
或者 File.WriteAllText 方法在一行中完成此操作,而无需 .using