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

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

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

问题:
由于某种原因,当配置文件不存在时,它是在配置序列化程序 class 的构造函数调用之前创建的。这让我和我的朋友们认为该文件是在项目的其他地方创建的。但事实并非如此,因为我使用了一个 control 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. 更改配置序列化程序的调用顺序和位置class。

    Result: Empty file with config name and path is still created right before the initialization of config serializer.

  2. 正在更改 _configPath 变量的路径。

    Result: Empty file created under new name.

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

    Result: No change.

注:

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

在我跟踪你的代码的小测试中,问题似乎出在 SaveConfig() 方法上。遍历代码应该会发现代码行… sw.Write(data); … 正在执行,但是实际上没有任何内容写入文件。为 StreamWriter 添加 Flush 语句应该确保…… sw.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 语句中。

进一步的测试表明,需要在 StreamWriter 周围包装一个 Using 语句,并且应该实施。因此,将 StreamWriter 包装在 using 语句中应该 Flush 适合您的数据。下面是使用 using 语句的更好方法。

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);
    }
  }
}