2015-05-23 2 views
2
var client = new WebClient(); 
var bytes = client.DownloadData(webUrl); <-- NOT null 

Application.Current.Dispatcher.BeginInvoke(new Action(() => 
{ 
    BitmapImage img = new BitmapImage(); 
    img.BeginInit(); 
    img.StreamSource = new MemoryStream(bytes); <-- null 
    img.EndInit(); 
    img_DownloadCompleted(img, webUrl); 
})); 

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

Я пытаюсь загрузить изображение из Интернета в объект BitmapImage. Изображение загружается правильно, но когда я пытаюсь использовать его в своем пользовательском интерфейсе (используя Dispatcher.Invoke), я получаю это сообщение об ошибке: The calling thread cannot access this object because a different thread owns it.

Итак, я добавил код, который создает изображение в потоке пользовательского интерфейса. Но теперь, когда код достигает линии, указанной <-- null, переменная bytes внезапно стала нулевой. Он не равен нулю, прежде чем выполнение переходит к анонимной функции. (Я проверил с отладчиком)

Кто-нибудь знает, почему это так? Google не очень помогает.

Изменение типа переменной bytes на var не имеет значения.

+0

Сниппет не дает никакого намека вообще. Вернитесь к исходному подходу, не забудьте вызвать метод Freeze() BitmapImage, чтобы больше не получить это исключение. –

+0

Спасибо! img.Freeze() после img.EndInit() действительно исправляет основную проблему. – Jan

ответ

5

Скорее всего, вы меняете bytes переменную позже и, следовательно, изменяете «захваченное» значение внутри анонимной функции. Что-то вроде:

var bytes = client.DownloadData(webUrl); <-- NOT null 
Application.Current.Dispatcher.BeginInvoke(new Action(() => 
{ 
... img.StreamSource = new MemoryStream(bytes); <-- null 
... 
} 
bytes = null; // something like this - because why not? 

Обратите внимание, что даже если код выглядит последовательным и img.StreamSource = ... является перед темbytes = null; линия на самом деле, вероятно, будет выполняться в обратном порядке (не детерминированным, как он работает на другом потоке).

Вы должны быть очень осторожны с такими захватами, которые будут выполняться асинхронно позже/в другой теме. Безопасный вариант заключается в создании анонимной функции внутри отдельного метода, так что вы не можете изменить захваченные переменные позже:

Action CreateBitmapAction(bytes[] bytes) 
{ 
return() => 
{ 
    BitmapImage img = new BitmapImage(); 
    img.BeginInit(); 
    img.StreamSource = new MemoryStream(bytes); 
    img.EndInit(); 
    img_DownloadCompleted(img, webUrl); 
}; 
} 

Application.Current.Dispatcher.BeginInvoke(CreateBitmapAction(bytes)); 
+0

Вы правы в своем предположении. В последней строке метода у меня было 'bytes = null;'. Я не знал, что анонимные методы могут быть выполнены из последовательности. В будущем я буду использовать ваше предложение и поместить его в метод обертки. Спасибо :) – Jan

+0

@Jan обратите внимание, что «вне последовательности» не имеет никакого отношения к тому, что это анонимный метод - это действительно функция, которая принимает делегат/лямбда в качестве аргумента, чтобы решить, когда (если когда-либо) вызывается метод. Вы также найдете аналогичное поведение с LINQ (поиск «ленивая оценка LINQ») и обработчики событий (что несколько проще понять) при добавлении обработчика «Click» нельзя ожидать немедленного вызова обработчика). –

1

Это вызвано закрытием созданного для метода. ссылается на объект, который находится за пределами его области. Ответ Алексея решит проблему.

, но если вы все еще wan't сохранить лямбда-подобный синтаксис в коде вы можете сделать то же самое, например, так:

var bytes = client.DownloadData(webUrl); 
    Application.Current.Dispatcher.BeginInvoke((Action<byte[]>)(b => 
    { 
     // draw a pink bunny , or what ever here. 
    }),bytes); 
Смежные вопросы