Делегатор конвертера помогает мне визуализировать обе совместно работающие концепции:
delegate TOutput Converter<in TInput, out TOutput>(TInput input);
TOutput
представляет ковариацию, в которой метод возвращает более конкретный тип .
TInput
представляет собой контравариантность, когда метод передается менее конкретным типом .
public class Dog { public string Name { get; set; } }
public class Poodle : Dog { public void DoBackflip(){ System.Console.WriteLine("2nd smartest breed - woof!"); } }
public static Poodle ConvertDogToPoodle(Dog dog)
{
return new Poodle() { Name = dog.Name };
}
List<Dog> dogs = new List<Dog>() { new Dog { Name = "Truffles" }, new Dog { Name = "Fuzzball" } };
List<Poodle> poodles = dogs.ConvertAll(new Converter<Dog, Poodle>(ConvertDogToPoodle));
poodles[0].DoBackflip();
Вам нужно один раз объявить и настроить BackgroundWorker - затем вызвать метод RunWorkerAsync в вашем цикле ...
public class UpdateController
{
private UserController _userController;
private BackgroundWorker _backgroundWorker;
public UpdateController(LoginController loginController, UserController userController)
{
_userController = userController;
loginController.LoginEvent += Update;
_backgroundWorker = new BackgroundWorker();
_backgroundWorker.DoWork += new DoWorkEventHandler(backgroundWorker_DoWork);
_backgroundWorker.ProgressChanged += new ProgressChangedEventHandler(backgroundWorker_ProgressChanged);
_backgroundWorker.WorkerReportsProgress= true;
}
public void Update()
{
_backgroundWorker.RunWorkerAsync();
}
public void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
while (true)
{
// Do the long-duration work here, and optionally
// send the update back to the UI thread...
int p = 0;// set your progress if appropriate
object param = "something"; // use this to pass any additional parameter back to the UI
_backgroundWorker.ReportProgress(p, param);
}
}
// This event handler updates the UI
private void backgroundWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
// Update the UI here
// _userController.UpdateUsersOnMap();
}
}
Вы должны использовать свойство Control.InvokeRequired , чтобы определить, находитесь ли вы в фоновом потоке. Затем вам нужно вызвать свою логику, которая изменила ваш пользовательский интерфейс, с помощью метода Control.Invoke , чтобы заставить ваши операции пользовательского интерфейса происходить в основном потоке. Вы делаете это путем создания делегата и передачи его методу Control.Invoke . Подвох здесь в том, что вам нужен какой-то объект, полученный из Control для вызова этих методов.
Редактировать : Как другой пользователь опубликовал сообщение, если вы можете подождать, пока событие BackgroundWorker.Completed обновит ваш пользовательский интерфейс, вы можете подписаться на это событие и напрямую вызвать свой код пользовательского интерфейса. , BackgroundWorker_Completed вызывается в главном потоке приложения. мой код предполагает, что вы хотите делать обновления во время операции. Одной из альтернатив моего метода является подписка на событие BwackgroundWorker.ProgressChanged , но я считаю, что вам все равно придется вызывать Invoke для обновления вашего интерфейса в этом случае.
например
public class UpdateController
{
private UserController _userController;
BackgroundWorker backgroundWorker = new BackgroundWorker();
public UpdateController(LoginController loginController, UserController userController)
{
_userController = userController;
loginController.LoginEvent += Update;
}
public void Update()
{
// The while loop was unecessary here
backgroundWorker.DoWork += new DoWorkEventHandler(backgroundWorker_DoWork);
backgroundWorker.RunWorkerAsync();
}
public delegate void DoUIWorkHandler();
public void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
// You must check here if your are executing on a background thread.
// UI operations are only allowed on the main application thread
if (someControlOnMyForm.InvokeRequired)
{
// This is how you force your logic to be called on the main
// application thread
someControlOnMyForm.Invoke(new
DoUIWorkHandler(_userController.UpdateUsersOnMap);
}
else
{
_userController.UpdateUsersOnMap()
}
}
}
Вы можете использовать событие RunWorkerCompleted в классе backgroundWorker, чтобы определить, что следует делать после завершения фоновой задачи. Итак, вы должны выполнить вызов базы данных в обработчике DoWork, а затем обновить интерфейс в обработчике RunWorkerCompleted, примерно так:
BackgroundWorker bgw = new BackgroundWorker();
bgw.DoWork += (o, e) => { longRunningTask(); }
bgw.RunWorkerCompleted += (o, e) => {
if(e.Error == null && !e.Cancelled)
{
_userController.UpdateUsersOnMap();
}
}
bgw.RunWorkerAsync();
В дополнение к предыдущим комментариям взгляните на www.albahari.com/threading - лучший документ по многопоточности, который вы когда-либо найдете. Он научит вас, как правильно использовать BackgroundWorker.
Вам следует обновить графический интерфейс, когда BackgroundWorker запускает событие Completed (которое вызывается в потоке пользовательского интерфейса, чтобы упростить вам задачу, чтобы вам не приходилось выполнять Control.Invoke самостоятельно).
Отражение - это один из способов сделать this ( SetField
).
Второй способ вызвать это любопытство - это если вы слишком рано раздадите ссылку, передав this
(или что-нибудь, включающее неявное this
, например, поле, захваченное анонимным методом / лямбда) из .ctor
:
public MyClass(int id)
{
Program.Test(this); // oopsie ;-p
HashPrefs = new Hashtable();
}
Или более вероятно (с учетом вопроса):
SomeQueue.Add(this);
Другой важный вопрос может быть - может это не должно быть присвоено в первую очередь? И ответ на это - да, особенно если вы используете сериализацию. Вопреки распространенному мнению, вы можете обойти конструкторы , если у вас есть причина;