静态变量初始化的意外行为

时间:2014-06-10 17:46:55

标签: c# windows-runtime winrt-async

我对WinRT不太熟悉。我遇到了意想不到的行为。我在类的静态构造函数中初始化了static变量_Verses。因此,_Verses的预期行为将在首次引用静态方法之前进行初始化,如When is a static constructor called in C#?中所述

但是当我调用static async函数LoadData(WinRT)时,我得到了异常。

  

对象引用未设置为对象的实例。

我的代码是:

public VerseCollection
{
   public const int TotalVerses = 6236;

   static Verse[] _Verses;
   static VerseCollection()
   {
        _Verses = new Verse[TotalVerses];
   }

   internal static async void LoadData(StorageFile file)
   {
      using (var reader = new BinaryReader(await file.OpenStreamForReadAsync()))
      {
           int wId = 0;
           for (int i = 0; i < VerseCollection.TotalVerses; i++)
           {
               var retValue = new string[reader.ReadInt32()];
               for (int j = 0; j < retValue.Length; j++)
                    retValue[j] = reader.ReadString();

               _Verses[i] = new Verse(i, wId, retValue);

               wId += _Verses[i].Words.Count;
           }
       }
   }
}

public Book
{    
   public static async Task<Book> CreateInstance()
   {
       VerseCollection.LoadData(await DigitalQuranDirectories.Data.GetFileAsync("quran-uthmani.bin"));
   }
}

我将函数CreateInstance称为:

async void DoInit()
{
    await DigitalQuran.Book.CreateInstance();
}

相同的代码在桌面上工作但不适用于WinRT。桌面的Book类的完整代码为here,而VerseCollection类的完整代码为here

修改 完整的代码在这里

public class Book : VerseSpan
{
    public static async Task<Book> CreateInstance()
    {
        _Instance = new Book();

        VerseCollection.LoadData(await DigitalQuranDirectories.Data.GetFileAsync("quran-uthmani.bin"));
        PrivateStorage.LoadQuranObjectsFromMetadata();
        // Some Other Operations too

        return _Instance;
    }
}


public class VerseCollection
{
    static Verse[] _Verses = new Verse[TotalVerses];

    internal static async void LoadData(StorageFile file)
    {
        using (var reader = new BinaryReader(await file.OpenStreamForReadAsync()))
        {
            int wId = 0;
            for (int i = 0; i < VerseCollection.TotalVerses; i++)
            {
                var retValue = new string[reader.ReadInt32()];
                for (int j = 0; j < retValue.Length; j++)
                    retValue[j] = reader.ReadString();

                _Verses[i] = new Verse(i, wId, retValue);

                wId += _Verses[i].Words.Count;
            }
        }
    }
}

public class Verse 
{
    public Verse(int number, int firstWordIndex, string[] words)
    {
        GlobalNumber = number + 1;

        Words = new WordCollection(firstWordIndex, words, this);            
    }
}

public class WordCollection : ReadOnlyCollection<Word>
{
    public const int TotalWords = 77878;

    static Word[] _Words = new Word[TotalWords];
    static string[] _WordsText = new string[TotalWords];

    public WordCollection(int startIndex, int count)
        : base(count)
    {
        this.startIndex = startIndex;
    }

    internal WordCollection(int startId, string[] words, Verse verse) : this(startId, words.Length)
    {
        int max = words.Length + startId;
        for (int i = startId; i < max; i++)
        {
            _Words[i] = new Word(i, verse);
            _WordsText[i] = words[i - startId];            
        }
    }
}

public abstract class ReadOnlyCollection<T> : IEnumerable<T>
{
    public ReadOnlyCollection(int count)
    {
        Count = count;
    }
}

public class PrivateStorage
{
    internal static async void LoadQuranObjectsFromMetadata()
    {            
        using (var reader = new BinaryReader(await (await DigitalQuranDirectories.Data.GetFileAsync(".metadata")).OpenStreamForReadAsync()))
        {
            /* 1 */ ChapterCollection.LoadData(EnumerateChapters(reader));
            /* 2 */ PartCollection.LoadData(EnumerateParts(reader));
            /* Some other tasks */
        }
    }

    static IEnumerator<ChapterMeta> EnumerateChapters(BinaryReader reader)
    {
        for (int i = 0; i < ChapterCollection.TotalChapters; i++)
        {
            yield return new ChapterMeta()
            {
                StartVerse = reader.ReadInt32(),
                VerseCount = reader.ReadInt32(),
                BowingCount = reader.ReadInt32(),
                Name = reader.ReadString(),
                EnglishName = reader.ReadString(),
                TransliteratedName = reader.ReadString(),
                RevelationPlace = (RevelationPlace)reader.ReadByte(),
                RevelationOrder = reader.ReadInt32()
            };
        }
    }

    static IEnumerator<PartMeta> EnumerateParts(BinaryReader reader)
    {
        for (int i = 0; i < PartCollection.TotalParts; i++)
        {
            yield return new PartMeta()
            {
                StartVerse = reader.ReadInt32(),
                VerseCount = reader.ReadInt32(),
                ArabicName = reader.ReadString(),
                TransliteratedName = reader.ReadString()
            };
        }
    }
}


public class ChapterCollection : ReadOnlyCollection<Chapter>
{
    public const int TotalChapters = 114;

    static Chapter[] _Chapters = new Chapter[TotalChapters];

    internal static void LoadData(IEnumerator<ChapterMeta> e)
    {
        for (int i = 0; i < TotalChapters; i++)
        {
            e.MoveNext();
            _Chapters[i] = new Chapter(i, e.Current);
        }
    }
}


public class PartCollection : ReadOnlyCollection<Part>
{
    public const int TotalParts = 30;

    static Part[] _Parts = new Part[TotalParts];
    internal static void LoadData(IEnumerator<PartMeta> e)
    {            
        for (int i = 0; i < TotalParts; i++)
        {
            e.MoveNext();
            _Parts[i] = new Part(i, e.Current);
        }
    }
}

当我使用调试器运行代码时,不会引发异常。此外,异常视觉工作室在VerseCollection的{​​{1}}课程中的某些时间显示LoadData_Verses[i] = new Verse(i, wId, retValue);_Verses),有时在课程{{1在nullChapterCollection上的LoadData _Chapters[i] = new Chapter(i, e.Current); {/ 1}}

2 个答案:

答案 0 :(得分:1)

异步调用存在问题。文件读取是WinRT中的异步操作。我们无法使用带有async语句的void返回类型调用await方法。因此,下一条指令无需等待上次执行完成另一个Task即可执行。这会导致NullReferanceExecption

我设法通过将所有async操作的返回类型从void更改为Task来解决我的问题,并使用await调用它们,如下面的代码所示。

public class Book : VerseSpan
{
    public static async Task<Book> CreateInstance()
    {
        _Instance = new Book();

        await VerseCollection.LoadData(await DigitalQuranDirectories.Data.GetFileAsync("quran-uthmani.bin"));
        await PrivateStorage.LoadQuranObjectsFromMetadata();
        // Some Other Operations too

        return _Instance;
    }
}


public class VerseCollection
{
    static Verse[] _Verses = new Verse[TotalVerses];

    internal static async Task LoadData(StorageFile file)
    {
        using (var reader = new BinaryReader(await file.OpenStreamForReadAsync()))
        {
            int wId = 0;
            for (int i = 0; i < VerseCollection.TotalVerses; i++)
            {
                var retValue = new string[reader.ReadInt32()];
                for (int j = 0; j < retValue.Length; j++)
                    retValue[j] = reader.ReadString();

                _Verses[i] = new Verse(i, wId, retValue);

                wId += _Verses[i].Words.Count;
            }
        }
    }
}

public class PrivateStorage
{
    internal static async Task LoadQuranObjectsFromMetadata()
    {            
        using (var reader = new BinaryReader(await (await DigitalQuranDirectories.Data.GetFileAsync(".metadata")).OpenStreamForReadAsync()))
        {
            /* Some tasks */
        }
    }
}

答案 1 :(得分:0)

因为它在桌面而不是WinRT上运行,所以它让我相信你的异步调用存在问题。因为您是异步执行此操作,所以没有任何保证在调用LoadData之前构造函数(静态或非静态)将完成运行。在调用LoadData函数之前,请确保构造函数已完成执行,这应该可以为您提供一致的行为。