是否无法从默认构造函数反序列化为 'this'?

Is it not possible to deserialize to 'this' from default constructor?

情况:

我有一个绑定到设置视图的 ViewModel,用户应该在其中输入许多个性化设置,并且应该能够将它们保存为预设并加载它们。为此,ViewModel 包含预设数据模型的集合,这些模型本身包含不同的属性、class 对象等。 计划是通过xml 序列化和反序列化整个ViewModel 来实现保存所有预设。

代码/问题

我从 ViewModel 的构造函数中调用了以下方法:

private void InitializePresetsFromFile()
{
    if (!File.Exists(Info.GetDefaultColorPalettePresetsXml()))
    {
        SetupNewEmpty();
        SerializePresets(Info.GetDefaultColorPalettePresetsXml());
    }
    else
    {
        DeserializePresets(Info.GetDefaultColorPalettePresetsXml());
    }
}

所以该方法检查保存预设的文件是否存在 - 如果不存在,它应该设置一个空预设并将其保存到新创建的文件中,否则它应该从现有文件加载预设。

序列化过程运行良好,但是由于我序列化为 this,反序列化出现问题:

private void DeserializePresets(string path)
{
    XmlSerializer deserializer = new XmlSerializer(typeof(LinearAxisColorPresetsViewModel));
    TextReader reader = new StreamReader(path);
    object obj = deserializer.Deserialize(reader);
    LinearAxisColorPresetsViewModel XmlData = (LinearAxisColorPresetsViewModel)obj;
    reader.Close();
    VolumePresetList = XmlData.VolumePresetList;
    WaveShapePresetList = XmlData.WaveShapePresetList;
    VolumePresetSelectedIndex = XmlData.VolumePresetSelectedIndex;
    WaveShapePresetSelectedIndex = XmlData.WaveShapePresetSelectedIndex;
}

这里的问题是,由于我直接从构造函数调用方法 InitializePresetsFromFile(),反序列化器在永无止境的循环中调用自己,导致计算器溢出错误。

所以,最简单的解决方案应该是使用另一个带参数的构造函数,我在这里调用InitializePresetsFromFile(),对吧?这里的问题是ViewModelclass是在对应View的xaml中直接实例化的:

<UserControl.Resources>
    <ResourceDictionary>
        <vm:LinearAxisColorPresetsViewModel x:Key="vm" />
    </ResourceDictionary>
</UserControl.Resources>

This posts 第二个答案指出约定是,从 XAML 调用的构造函数应该是无参数的,我想坚持这一点。

问题:

问题只是如何根据最佳实践解决这个问题。由于这是我第一次尝试序列化和反序列化,我担心我在这里走错了路。我的感觉是只有数据模型 classes 应该被序列化。我的 ViewModel 包含两个这样的 classes 的 ObservableCollection,但是我想序列化完整的集合以及 ViewModel 中的其他属性,例如所选索引。

您确实已经到了必须决定如何继续的地步。你现在所做的是行不通的。 XML 序列化程序和 XAML 在这种情况下都使用默认构造函数。你不能让它在这里有两个用途。

我的建议是创建一个 class 来反映您用于反序列化 XML 文件的视图模型的属性。这个class只需要属性,仅此而已。

如果视图模型 class 实际上是静态的,您可以使用 a locator class 将其绑定到。

首先,您不应该在 class' 构造函数中调用 InitializePresetsFromFile 方法。构造函数必须尽可能快并且不应该引起副作用。在构造函数中读取文件是一种不好的做法:如果不访问文件系统,就无法创建 class 的实例。这意味着您的代码不可测试,容易出错(例如,您是否考虑过突然的 UnauthorizedAccessExceptions?),而且速度很慢。

相反,创建一个 public 方法来反序列化文件中的数据。这将打破你的无限递归。

如何调用该方法?

  • 您真的需要资源字典中的 LinearAxisColorPresetsViewModel 实例吗?如果没有,只需将反序列化实例分配给视图的 DataContext 属性。
  • 如果确实需要,请在视图模型中创建一个 ICommand,例如InitializeCommand 使用上述方法从文件初始化内部状态;在 app startup/view show-up 等上执行该命令。您可以使用例如InvokeCommandAction 用于 Loaded 事件。