【问题标题】:Is it not possible to deserialize to 'this' from default constructor?不能从默认构造函数反序列化为“this”吗?
【发布时间】:2019-03-04 13:55:45
【问题描述】:

情况:

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

代码/问题

我从 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(),对吧?这里的问题是ViewModel类是直接在对应View的xaml里面实例化的:

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

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

问题:

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

【问题讨论】:

    标签: c# .net serialization xml-serialization


    【解决方案1】:

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

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

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

    【讨论】:

    • 嗯,我担心这个答案......我会看看你建议使用定位器类,看看我是否可以在我的情况下实现它。谢谢!
    • 会使用this 方法来找出谁在调用构造函数并且只调用InitializePresetsFromFile(); 好的做法吗?我仍然需要对其进行测试,但我想它应该可以工作..
    • 我不建议这样做。让下一位看到该代码的同事了解它的功能并且不做这些晦涩难懂的事情......
    【解决方案2】:

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

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

    如何调用该方法?

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

    【讨论】:

    • 这些是有效点。我可能会尝试按照 Patrick Hofman 的建议将序列化所需的所有数据放在它自己的类中,并按照您的建议从 loaded 事件开始反序列化过程。
    猜你喜欢
    • 1970-01-01
    • 2018-08-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-11-05
    • 1970-01-01
    相关资源
    最近更新 更多