код c#, кажется, оптимизирован недопустимым способом, таким образом, что объектное значение становится пустым

У меня есть следующий код, который показывает странную проблему:

var all = new FeatureService().FindAll();
System.Diagnostics.Debug.Assert(all != null, "FindAll must not return null");
System.Diagnostics.Debug.WriteLine(all.ToString()); // throws NullReferenceException

Подпись метода FindAll:

public List<FeatureModel> FindAll()

При продвижении через код, я подтвердил, что возвращаемое значение от FindAll не является пустым, и как Вы видите от Утверждения, "вся" переменная не является пустой, еще в следующей строке, это, кажется, является пустым.

Вопрос не характерен для сбоя, когда ToString () метод называют. Я упростил его вниз до этого восстанавливаемого примера при попытке проследить первопричину.

Это может быть подсказкой: в отладчике переменная "все" появляется в окне Locals со значением, "Не может получить значение локальных или аргумента 'все', поскольку это не доступно в этом указателе команд, возможно потому что это было оптимизировано далеко".

Я считал попытку одного из подходов зарегистрированной в другом месте для отключения оптимизации кода, но это действительно не решит проблему, так как версия выпуска кода будет все еще оптимизирована.

Я использую Visual Studio 2010 с.NET 4.0.

Какие-либо мысли?

ОБНОВЛЕНИЕ: на запрос вот весь метод:

protected override List<FeatureModel> GetModels() {
    var all = new FeatureService().FindAll();
    var wr = new WeakReference(all);
    System.Diagnostics.Debug.Assert(all != null, "FindAll must not return null");
    System.Diagnostics.Debug.WriteLine(wr.IsAlive);
    System.Diagnostics.Debug.WriteLine(all.ToString()); // throws NullReferenceException
    return all;
}

Как к вашему сведению, исходная реализация была просто:

protected override List<FeatureModel> GetModels() {
    return new FeatureService().FindAll();
}

Я первоначально встретился с пустой исключительной ситуацией в вызывающем методе. Код, который я отправил, после прослеживал проблему некоторое время.

ОБНОВЛЕНИЕ № 2: Согласно просьбе вот отслеживание стека от исключения:

 at FeatureCrowd.DomainModel.FeatureSearch.GetModels() in C:\Users\Gary\Documents\Visual Studio 2010\Projects\FeatureCrowd\FeatureCrowd.DomainModel\FeatureSearch.cs:line 32
 at FeatureCrowd.DomainModel.FeatureSearch.CreateIndex() in C:\Users\Gary\Documents\Visual Studio 2010\Projects\FeatureCrowd\FeatureCrowd.DomainModel\FeatureSearch.cs:line 42
 at FeatureCrowd.DomainModel.FeatureService.CreateSearchIndex() in C:\Users\Gary\Documents\Visual Studio 2010\Projects\FeatureCrowd\FeatureCrowd.DomainModel\FeatureService.cs:line 100
 at Website.MvcApplication.BuildLuceneIndexThread(Object sender) in C:\Users\Gary\Documents\Visual Studio 2010\Projects\FeatureCrowd\FeatureCrowd.Website\Global.asax.cs:line 50
 at Website.MvcApplication.Application_Start() in C:\Users\Gary\Documents\Visual Studio 2010\Projects\FeatureCrowd\FeatureCrowd.Website\Global.asax.cs:line 61
15
задан John Saunders 15 May 2014 в 02:39
поделиться

4 ответа

После того, как Лассе обнаружил, что метод FindAll генерирует неправильный IL, я наткнулся на другой метод, который также генерирует неправильный IL - я также нашел первопричину и решение.

Соответствующая строка во втором методе:

var policy = Cache.GetDefaultCacheItemPolicy(dependentKeys, true);

Cache - это мой собственный объект. Метод GetDefaultCacheItemPolicy возвращает объект System.Runtime.Caching.CacheItemPolicy. Сгенерированный IL, однако, выглядел так:

Func<Feature, FeatureModel> policy = (Func<Feature, FeatureModel>) base.Cache.GetDefaultCacheItemPolicy(dependentKeys, true);

Здесь в игре два проекта. Методы, генерирующие неправильный IL, находятся в одном проекте под названием DomainModel, а объект Cache находится в проекте Utilities, на который ссылается первый проект. Второй проект содержит ссылку на System.Runtime.Caching, а первый - нет.

Исправление заключалось в добавлении ссылки на System.Runtime.Caching в первый проект. Теперь сгенерированный IL выглядит правильно:

CacheItemPolicy policy = base.Cache.GetDefaultCacheItemPolicy(dependentKeys, true);

Первый метод (о котором Lasse написал в своем ответе) теперь также генерирует правильный IL.

Ура!

8
ответ дан 1 December 2019 в 01:53
поделиться

Если вы подозреваете, что переменная каким-то образом оптимизирована, вы можете проверить это, используя свойство IsAlive объекта WeakReference на ваш объект all или через GC.KeepAlive (all) . Я не уверен, что это будет полезно, но, возможно, стоит попробовать.

Другая возможность, хотя и весьма маловероятная, заключается в том, что по какой-то причине List .ToString вызывает это исключение. Вы могли бы проверить это с помощью такого инструмента, как .NET Reflector, чтобы увидеть, что именно делает этот метод.

0
ответ дан 24 October 2019 в 04:53
поделиться

Оставлено для потомков, проблема не в этом.

См. Мой новый ответ .


Вот во что я верю.

Вопреки тому, что вы говорите, я считаю, что программа на самом деле не дает сбой ни в одной из опубликованных строк, а вместо этого дает сбой в одной из следующих за ними строк, которые вы еще не опубликовали.

Причина, по которой я считаю это, заключается в том, что я также считаю, что вы делаете Release-build, и в этом случае обе строки Debug будут удалены, поскольку они помечены [Conditional ("DEBUG")] атрибут.

Подсказка здесь в том, что переменная all была оптимизирована, и это должно происходить только во время сборки выпуска, а не сборки отладки.

Другими словами, я считаю, что переменная all в конце концов имеет значение actall null , а строки отладки не выполняются, потому что они не скомпилированы в сборку. Отладчик послушно сообщает, что переменная all больше не существует.

Обратите внимание, что все это должно быть легко проверить. Просто установите точку останова на первой из двух опубликованных вами строк отладки. Если точка останова достигнута, моя гипотеза, скорее всего, неверна. Если это не так (и я собираюсь предположить, что символ точки останова отображается как полый кружок во время выполнения), то эти строки не компилируются в сборку.

2
ответ дан 1 December 2019 в 01:53
поделиться

После просмотра кода через TeamViewer и, наконец, загрузки, компиляции и запуска кода на моем собственном компьютере, я полагаю , что это ошибка компилятора. в C # 4.0 .


Я разместил вопрос с запросом на проверку после того, как сумел свести проблему к нескольким простым проектам и файлам. Он доступен здесь: Возможная ошибка компилятора C # 4.0, могут ли другие проверить?


Вероятная причина не в этом методе:

protected override List<FeatureModel> GetModels() {
    var fs = new FeatureService();
    var all = fs.FindAll();
    var wr = new WeakReference(all);
    System.Diagnostics.Debug.Assert(all != null, "FindAll must not return null");
    System.Diagnostics.Debug.WriteLine(wr.IsAlive);
    System.Diagnostics.Debug.WriteLine(all.ToString()); // throws NullReferenceException
    return all;
}

Но в методе, который он вызывает, FeatureService.FindAll:

public List<FeatureModel> FindAll() {
    string key = Cache.GetQueryKey("FindAll");
    var value = Cache.Load<List<FeatureModel>>(key);
    if (value == null) {
        var query = Context.Features;
        value = query.ToList().Select(x => Map(x)).ToList();
        var policy = Cache.GetDefaultCacheItemPolicy(value.Select(x => Cache.GetObjectKey(x.Id.ToString())), true);
        Cache.Store(key, value, policy);
    }
    value = new List<FeatureModel>();
    return value;
}

Если я изменил вызов в GetModels от этого:

var all = fs.FindAll();

до этого:

var all = fs.FindAll().ToList(); // remember, it already returned a list

тогда программа вылетает с ExecutionEngineException .


После очистки, сборки и последующего просмотра скомпилированного кода с помощью Reflector, вот как выглядит результат (прокрутите код до конца, чтобы увидеть важную часть):

public List<FeatureModel> FindAll()
{
    List<FeatureModel> value;
    Func<FeatureModel, string> CS$<>9__CachedAnonymousMethodDelegate6 = null;
    List<FeatureModel> CS$<>9__CachedAnonymousMethodDelegate7 = null;
    string key = base.Cache.GetQueryKey("FindAll");
    if (base.Cache.Load<List<FeatureModel>>(key) == null)
    {
        if (CS$<>9__CachedAnonymousMethodDelegate6 == null)
        {
            CS$<>9__CachedAnonymousMethodDelegate6 = (Func<FeatureModel, string>) delegate (Feature x) {
                return this.Map(x);
            };
        }
        value = base.Context.Features.ToList<Feature>().Select<Feature, FeatureModel>(((Func<Feature, FeatureModel>) CS$<>9__CachedAnonymousMethodDelegate6)).ToList<FeatureModel>();
        if (CS$<>9__CachedAnonymousMethodDelegate7 == null)
        {
            CS$<>9__CachedAnonymousMethodDelegate7 = (List<FeatureModel>) delegate (FeatureModel x) {
                return base.Cache.GetObjectKey(x.Id.ToString());
            };
        }
        Func<Feature, FeatureModel> policy = (Func<Feature, FeatureModel>) base.Cache.GetDefaultCacheItemPolicy(value.Select<FeatureModel, string>((Func<FeatureModel, string>) CS$<>9__CachedAnonymousMethodDelegate7), true);
        base.Cache.Store<List<FeatureModel>>(key, value, (CacheItemPolicy) policy);
    }
    value = new List<FeatureModel>();
    bool CS$1$0000 = (bool) value;
    return (List<FeatureModel>) CS$1$0000;
}

Обратите внимание на 3 последние строки метод, вот как они выглядят в коде:

value = new List<FeatureModel>();
return value;

вот что говорит Reflector:

value = new List<FeatureModel>();
bool CS$1$0000 = (bool) value;
return (List<FeatureModel>) CS$1$0000;

Он создает список, затем преобразует его в логическое значение, затем возвращает его обратно в список и возвращает его. Скорее всего, это вызывает проблему со стеком.

Вот тот же метод в IL (все еще через Reflector), я убрал большую часть кода:

.method public hidebysig instance class [mscorlib]System.Collections.Generic.List`1<class FeatureCrowd.DomainModel.FeatureModel> FindAll() cil managed
{
    .maxstack 5
    .locals init (
        [0] string key,
        [1] class [mscorlib]System.Collections.Generic.List`1<class FeatureCrowd.DomainModel.FeatureModel> 'value',
        [2] class [System.Data.Entity]System.Data.Objects.ObjectSet`1<class FeatureCrowd.DomainModel.Feature> query,
        [3] class [mscorlib]System.Func`2<class FeatureCrowd.DomainModel.Feature, class FeatureCrowd.DomainModel.FeatureModel> policy,
        [4] class [mscorlib]System.Func`2<class FeatureCrowd.DomainModel.FeatureModel, string> CS$<>9__CachedAnonymousMethodDelegate6,
        [5] class [mscorlib]System.Collections.Generic.List`1<class FeatureCrowd.DomainModel.FeatureModel> CS$<>9__CachedAnonymousMethodDelegate7,
        [6] bool CS$1$0000,
        [7] char CS$4$0001)
    ...
    L_009f: newobj instance void [mscorlib]System.Collections.Generic.List`1<class FeatureCrowd.DomainModel.FeatureModel>::.ctor()
    L_00a4: stloc.1 
    L_00a5: ldloc.1 
    L_00a6: stloc.s CS$1$0000
    L_00a8: br.s L_00aa
    L_00aa: ldloc.s CS$1$0000
    L_00ac: ret 
}

Вот скринкаст, показывающий сеанс отладки , если вам просто нужен результат Reflector , перейдите примерно к 2:50.

22
ответ дан 1 December 2019 в 01:53
поделиться
Другие вопросы по тегам:

Похожие вопросы: