2016-12-22 3 views
0

Я сейчас что-то кодирую, и передаю строку конструктору. Способ, которым генерируется строка, никоим образом не изменяется, но он (когда я запускаю инструменты отладки в Visual Studio Community) теряет значение в первый раз, но показывает значение в большинстве других случаев. С перерывами значение сообщает, что строка имеет значение NULL или значение, которое оно должно быть.Потеря значения аргумента

Теперь я действительно не знаю, как точно документировать, что я делаю, поэтому вот основные сведения.

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

FINAL, РАБОЧИЙ, NO-обесценились версия

public class TempDir : IDisposable 
{ 
    private readonly string _path; 
    public string ActiveDirectory => _path.Substring(_path.LastIndexOf('/') + 1, (_path.Length - _path.LastIndexOf('/') - 1)); 

    public string Path 
    { 
     get 
     { 
      return _path; 
     } 
    } 
    public TempDir(string path) : this(path, false) { } 
    public TempDir(string path, bool KillExisting) 
    { 
     _path = path; 
     if(!KillExisting) 
      return; 
     if(Directory.Exists(_path)) 
      Directory.Delete(_path); 
    } 
    public void Dispose() 
    { 
     if(System.IO.Directory.Exists(_path)) 
      Directory.Delete(_path, true); 
    } 

    public static implicit operator String(TempDir dir) => dir._path; 
} 

Теперь, это код, который я посылаю к конструктору. ActiveDirectory TempDir отправляется в конструктор, где NameOfThing должен быть результатом первого аргумента, а второй аргумент также является строкой. Первый работает с перерывами, второй всегда работает.

TempDir dir = new TempDir(Environment.GetFolderPath(Environment.SpecialFolders.LocalApplicationData) + "/First/Second/NameOfThing") 

This is the first run This is the run immediately after

Я серьезно так потерял на этом, и от того, что я могу сказать, я думаю, что поток может быть что-то на меня меняется без моего ведома

EDIT:

Теперь я могу «надежно» заставить его проходить каждый раз, но мне приходится медленно проходить через каждую строку кода. Запуск его обычно без отладки через каждую строку терпит неудачу каждый раз, но отладка медленно заставляет ее проходить каждый раз.

Код построения TEMPDIR:

protected static string PackagesLocation = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData) + "/Sloth/Notes/"; 
protected static TempDir TestPackLoc = new TempDir(PackagesLocation + "NPackageTests"); 
protected static NPackage TestPack = new NPackage(TestPackLoc.ActiveDirectory); 

Метод испытания создания страницы

[TestMethod] 
public void GeneratesLayoutAndResourcesDirectory() 
{ 
    string key = "GeneratesLayoutAndResourcesDictionary"; 
    TestPack.CreatePage(key); 
    if(!Directory.Exists(TestPackLoc + "/" + key + "/res") && !Directory.Exists(TestPackLoc + "/" + key + "/layout.xml")) 
     Assert.Fail(); 
} 

Итак, поведение утраченной ценности было, я думаю, потому что C# звала сборщик мусора некстати. @mason упомянул, что для типа TempDir вместо реализации деструктора я должен реализовать IDisposable. Теперь он работает надежно и последовательно. Я понятия не имею, почему реализация деструктора сделала это, но замена его на IDisposable работает просто отлично.

Кредит на решение идет в @mason

+0

Где находится NPage? Очевидно, что передача нулевого значения –

+1

требует более уместного кода, скриншотов недостаточно. – Fredou

+0

Необходим еще * соответствующий код –

ответ

1

Деструктор не является необходимым здесь. Вместо этого можно использовать шаблон IDisposable.Там больше, чем просто реализация интерфейса, вам также нужно правильно обрабатывать объект на любом объекте, который использует ваш IDisposable. Вы можете реализовать using statement.

using(var tempDir = new TempDir(arguments)) 
{ 
    //you can use tempDir inside here 
} //tempDir's Dispose method is automatically called here 
//since tempDir is out of scope here, the directory will have been deleted already 

Всякий раз, когда объект реализует IDisposable, вы должны обернуть его в using заявление, как и выше, или вызвать его метод Dispose в finally блоке, чтобы убедиться, что он получает должным образом удалены. Вот то, что попытка/поймать/наконец версия будет, как:

TempDir tempDir = null; 

try 
{ 
    tempDir = new TempDir(arguments); 
    //now you can use tempDir here 
} 
catch(Exception ex) 
{ 
    //log the exception. Optionally rethrow. Do not leave catch block empty 
} 
finally 
{ 
    if(tempDir != null) 
    { 
     tempDir.Dispose(); 
    } 
} 

Большую часть времени я предпочитаю using блок, потому что это делает сферу переменной понятнее.

Вы могли также использовать деструктор, чтобы убедиться, что если кто-то забывает позвонить Dispose или обернуть объект в using блоке, неуправляемый ресурс (каталог) получает очищенную должным образом.

public class TempDir : IDisposable 
{ 
    private readonly string _path; 

    public string ActiveDirectory => _path.Substring(_path.LastIndexOf('/') + 1, (_path.Length - _path.LastIndexOf('/') - 1)); 

    public string Path => _path; 

    public TempDir(string path) : this(path, false) { } 

    public TempDir(string path, bool KillExisting) 
    { 
     if(string.IsNullOrEmpty(path)) 
     { 
      throw new ArgumentException($"{nameof(path)} cannot be null or empty."); 
     } 

     _path = path; 

     if(KillExisting && Directory.Exists(_path)) 
     { 
      Directory.Delete(_path); 
     } 

     //why not call Directory.CreateDirectory(_path) here? 
    } 

    public void Dispose() 
    { 
     Cleanup(); 
    } 

    ~TempDir() 
    { 
     Cleanup(); 
    } 

    private void Cleanup() 
    { 
     if(Directory.Exists(_path)) 
     { 
      Directory.Delete(_path, true); 
     } 
    } 

    public static implicit operator String(TempDir dir) => dir._path; 
} 

Там нет необходимости вручную устанавливать объекты null, как сборщик мусора будет обрабатывать де-выделяющий память для вас.

0

Вы не герметизирующего личные переменные должным образом. Возможно, вы используете переменную public _path вне своего класса и непреднамеренно устанавливаете ее на что-то другое.

Улучшенная версия вашего класса TEMPDIR может выглядеть следующим образом:

public class TempDir 
{ 
    private string _path; 

    public TempDir(string path) 
     : this(path, false) { } 

    public TempDir(string path, bool killExisting) 
    { 
     _path = path; 

     if (!killExisting) return; 

     if (Directory.Exists(_path)) 
      Directory.Delete(_path); 
    } 

    public string Path => _path; 

    public string ActiveDirectory 
     => _path.Substring(_path.LastIndexOf('/') + 1, 
      _path.Length - _path.LastIndexOf('/') - 1); 

    ~TempDir() 
    { 
     if (System.IO.Directory.Exists(_path)) 
      Directory.Delete(_path, true); 
     _path = null; 
    } 

    public static implicit operator string(TempDir dir) 
    { 
     return dir._path; 
    } 
} 
+0

Горячий проклятый, это какой-то чистый код. Можете ли вы рассказать о свойствах? Я видел только, что используется для делегатов или LINQ (LINQ - это то, что я хочу избежать, как чума, потому что я видел, как HUGE падает производительность с помощью LINQ) –

+0

@JonathanSchmold Это синтаксис C# 6.0 для 'public string Path {get {return_path; }} ' – Maarten

+0

@ wj7ster Еще лучше было бы сделать поле' path' 'readonly'. – Maarten