Windows RT - StorageFile 的同步使用

Fre*_*chs 3 c# async-await windows-runtime storagefile

我想编写一个 Win8 应用程序,在那里我使用一个Configuration带成员的类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
    }
}
Run Code Online (Sandbox Code Playgroud)

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

Dam*_*Arh 5

设计决策不允许任何可能花费超过 50 毫秒的同步调用(即任何文件或网络 IO 调用),以使应用程序响应更快。

尽管您无法await从构造函数进行异步调用,但没有什么可以阻止您在不等待它们完成的情况下触发此类调用:

private Configuration()
{
    Init();
}

private async void Init()
{
    var contents = await FileIO.ReadTextAsync(file);
}
Run Code Online (Sandbox Code Playgroud)

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

如果您需要在应用程序中的某个时刻检查或等待操作完成,您可以更改Init()return的签名Task

private Configuration()
{
    InitTask = Init();
}

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

public Task InitTask { get; private set; }
Run Code Online (Sandbox Code Playgroud)

现在您可以检查是否已完成:

if (Configuration.Current.IsCompleted)
{
    //
}
Run Code Online (Sandbox Code Playgroud)

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

await Configuration.Current.InitTask;
Run Code Online (Sandbox Code Playgroud)

编辑:

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

  • 实际视图(您希望用户在应用程序准备就绪时看到)
  • 启动屏幕视图(例如显示进度指示器)

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>
Run Code Online (Sandbox Code Playgroud)