По вашему вопросу ...
Как передать параметры методу Thread.ThreadStart () в C #?
... и возникшей ошибкеВы должны будете исправить свой код от
Thread thread = new Thread(new ThreadStart(download(filename));
до
Thread thread = new Thread(new ThreadStart(download));
thread.Start(filename);
Однако вопрос более сложен, как кажется на первый взгляд.
Класс Thread
в настоящее время (4.7.2) предоставляет несколько конструкторов и метод Start
с перегрузками.
Этими соответствующими конструкторами для этого вопроса являются:
public Thread(ThreadStart start);
и
public Thread(ParameterizedThreadStart start);
, которые либо принимают делегата ThreadStart
, либо ParameterizedThreadStart
делегат.
Соответствующие делегаты выглядят так:
public delegate void ThreadStart();
public delegate void ParameterizedThreadStart(object obj);
Итак, как видно, правильный конструктор, который нужно использовать, - это тот, который принимаетParameterizedThreadStart
делегат, чтобы какой-либо метод, соответствующий указанной подписи делегата, мог быть запущен потоком.
Простым примером создания экземпляра класса Thread
будет
Thread thread = new Thread(new ParameterizedThreadStart(Work));
или просто
Thread thread = new Thread(Work);
Подпись соответствующего метода (в данном примере называемого Work
) выглядит следующим образом:
private void Work(object data)
{
...
}
Осталось только запустить поток.Это делается с помощью
public void Start();
или
public void Start(object parameter);
Хотя Start()
запускает поток и передает null
в качестве данных в метод, можно использовать Start(...)
передать что-нибудь в метод Work
потока.
Однако у этого подхода есть одна большая проблема: все, что передается в метод Work
, преобразуется в объект.Это означает, что в методе Work
его необходимо снова привести к исходному типу, как в следующем примере:
public static void Main(string[] args)
{
Thread thread = new Thread(Work);
thread.Start("I've got some text");
Console.ReadLine();
}
private static void Work(object data)
{
string message = (string)data; // Wow, this is ugly
Console.WriteLine($"I, the thread write: {message}");
}
Кастинг - это то, что вы обычно не хотитеделать.
Что если кто-то пропустит что-то еще, не являющееся строкой?Поначалу это кажется невозможным (потому что Это мой метод, я знаю, что я делаю или Этот метод является закрытым, как кто-то может когда-либо передавать ему что-либо? )Вы можете, возможно, в конечном итоге именно этот случай по разным причинам.Поскольку некоторые случаи не могут быть проблемой, другие - нет.В таких случаях вы, вероятно, в итоге получите InvalidCastException
, который вы, вероятно, не заметите, поскольку он просто завершает поток.
В качестве решения вы ожидаете получить универсальный ParameterizedThreadStart
делегировать как ParameterizedThreadStart<T>
, где T
будет типом данных, которые вы хотите передать в метод Work
.К сожалению, что-то вроде этого не существует (пока?).
Однако существует предлагаемое решение для этой проблемы.Он включает в себя создание класса, который содержит как данные, передаваемые в поток, так и метод, представляющий рабочий метод:
public class ThreadWithState
{
private string message;
public ThreadWithState(string message)
{
this.message = message;
}
public void Work()
{
Console.WriteLine($"I, the thread write: {this.message}");
}
}
При таком подходе вы можете запустить поток следующим образом:
ThreadWithState tws = new ThreadWithState("I've got some text");
Thread thread = new Thread(tws.Work);
thread.Start();
Таким образом, вы просто избегаете обхода и имеете безопасный для передачи данных потоку; -)