【问题标题】:Windows RT - synchronous usage of StorageFileWindows RT - StorageFile 的同步使用
【发布时间】:2013-04-29 08:54:03
【问题描述】:

我想编写一个 Win8 应用程序,我使用一个 Configuration-class 和一个 static 成员,它在启动时加载一次,并且可以在运行时从任何地方访问。

所以,主要问题是,默认设置存储在一个xml文件中,但是读取文件内容是异步的(StorageFile),但是我没有找到任何解决方案等待,直到文件完全加载,因为不可能在每种情况下(主线程、构造函数)都使用await,这在我看来是一个设计问题。 如果在访问Configuration 数据之前未完全读取文件,则此应用将出现错误行为。

这是一个示例代码:

public class Configuration
{
    // stores the only instance of this class
    private Configuration instance = null;

    // public access to the instance
    public Configuration Current
    {
        get
        {
            if (instance == null)
            {
                instance = new Configuration();
            }

            return instance;
        }
    }

    private Configuration()
    {
        // load data from file synchronously
        // so it is loaded once on first access
    }
}

我不确定,如何解决这个问题,可能我需要更改我的Configuration-class 的设计。任何建议/提示都会很好。

【问题讨论】:

    标签: c# windows-runtime async-await storagefile


    【解决方案1】:

    这是一项设计决定,不允许任何可能花费超过 50 毫秒的同步调用,即任何文件或网络 IO 调用,以提高应用程序的响应速度。

    虽然你不能await来自构造函数的异步调用,但没有什么能阻止你在不等待它们完成的情况下触发此类调用:

    private Configuration()
    {
        Init();
    }
    
    private async void Init()
    {
        var contents = await FileIO.ReadTextAsync(file);
    }
    

    然后您可以从Init() 内部设置Configuration 属性。如果您实现INotifyPropertyChanged,您可以在加载之前将值绑定到 UI,并且一旦加载,UI 就会刷新。

    如果您需要在应用中的某个时间点检查或等待操作完成,您可以将Init() 的签名更改为返回Task

    private Configuration()
    {
        InitTask = Init();
    }
    
    private async Task Init()
    {
        var contents = await FileIO.ReadTextAsync(file);
    }
    
    public Task InitTask { get; private set; }
    

    现在可以检查是否完成了:

    if (Configuration.Current.IsCompleted)
    {
        //
    }
    

    甚至await它(如果Task已经完成,这将立即完成):

    await Configuration.Current.InitTask;
    

    编辑:

    如果在加载所述文件之前向用户显示任何内容没有意义,您可以修改您的入口页面以具有替代“视图”:

    • 实际视图(您希望用户看到应用何时准备就绪)
    • 闪屏视图(例如显示进度指示器)

    您可以根据IsCompleted 属性使正确的可见,您应该在您的视图模型上公开该属性并实现INotifyPropertyChanged

    然后您可以按如下方式设计您的页面:

    <Page xmlns:common="using:MyApp.Common">
        <Page.Resources>
            <common:BooleanToVisibilityConverter x:Key="BoolToVisibilityConverter" />
        </Page.Resources>
        <Grid>
            <Grid Visibility="{Binding Configuration.IsCompleted, Converter={StaticResource BoolToVisibilityConverter}">
                <!-- put your actual view here -->
            </Grid>
            <Grid Visibility="{Binding Configuration.IsNotCompleted, Converter={StaticResource BoolToVisibilityConverter}">
                <!-- put your splash screen view here -->
            </Grid>
        </Grid>
    </Page>
    

    【讨论】:

    • 感谢您的回答,我已经尝试使用 INotifyPropertyChanged,但在我看来我没有帮助。在加载此文件之前显示我的应用程序的任何页面都没有任何意义。也许可以加载文件,同时显示启动画面,而当加载文件时,启动画面消失?任何提示,是否可行?
    • @FrecherxDachs 我在回答中添加了另一个建议。
    • 感谢您的提示,我会按照您描述的方式进行操作。该文件非常小,因此不需要进度条,但如果没有您的调整,有时会由于竞争条件而导致错误行为。非常感谢,祝你有美好的一天。 :)
    【解决方案2】:

    您不需要其他类来存储设置 - 为每个 Windows 8 应用程序提供了 AplicationData.Current.LocalSettings 字典,该字典在每次启动应用程序时自动加载,并且您在没有 await 关键字的情况下阅读它 - 例如:

    var settings = ApplicationData.Current.LocalSettings;
    settings.Values["MyIndex"] = 2;
    
    object value = settings.Values["MyIndex"];
    if (value != null)
    {
         int myIndex = (int)value;
         //...
    }
    

    【讨论】:

    • 谢谢,抱歉这个错误,我已经使用 ApplicationData.Current.LocalSettings 来存储我的设置。在我上面写的例子中,我想说,这是我对这个应用程序的默认配置(静态数据/字符串,它们是恒定的,永远不会改变,只有当我更新这个应用程序时)。 :: 我将示例类的名称更改为 Configuration,以使其更清晰。
    • @FrecherxDachs - 如果您已经在使用它,请更新您的问题。
    • ApplicationData.Current.LocalSettings 的使用不是我的意思,它只是关于如何从文件中加载数据并在启动时填充静态类的属性
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-11-04
    • 1970-01-01
    • 1970-01-01
    • 2012-10-06
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多