我创建了一个抽象类,它实现了一个在单独的线程上运行另一个抽象方法的方法,如下所示:
public abstract class ATest
{
Thread t;
protected String status;
public void Start()
{
ThreadStart ts = new ThreadStart(PerformTest);
t = new Thread(ts);
t.Start();
}
public ATest(String status)
{
this.status=status;
}
public abstract void PerformTest();
}
这个想法是从ATest派生的类只实现了PerformTest方法。因此,任何客户端都可以调用Start()方法在其自己的线程上的PerformTest()内启动操作。派生自ATest的类可能如下:
class ConcreteTest:ATest
{
public ConcreteTest(String status):base(status)
{
}
public override void PerformTest()
{
// Do some things...
// And some more...
status = "changed";
}
}
创建ConcreteTest对象时,我想传入一些对象(在示例中为String)。当PerformTest()在其单独的线程上运行时,将根据PerformTest()中的操作结果更改此对象的状态。使用上述类的示例:
class Program
{
static void Main(string[] args)
{
String isPassed = "original";
ATest test = new ConcreteTest(isPassed);
test.Start();
Console.WriteLine(isPassed); // Prints out "original"
}
}
所以我将isPassed设置为“originial”并将其传递给ConcreteTest,后者在一个单独的线程上将值更改为“changed”。因此,当我打印出isPassed时,我希望将值设置为“已更改”,但事实证明它不是。我想这与在最初创建它的另一个线程上更改的值有关。任何人都可以向我解释为什么我会得到这种行为,也许我可以做些什么来实现我正在寻找的功能(即更改isPassed在单独的线程上,以便当线程完成时,控制台将打印出来“改变”?
答案 0 :(得分:1)
字符串是不可变的。
当您编写status = "changed"
时,您不会更改任何现有的字符串对象
相反,您正在更改status
字段以指代完全不同的字段
String
实例。
status
字段与isPassed
方法中的Main
变量无关。当您编写new ConcreteTest(isPassed)
时,您将isPassed
变量的值传递给构造函数。
该参数与传递给它的变量无关,除了现在,它们碰巧引用同一个对象。您的status
字段也是如此。
最简单的方法是定义一个持有者类型,如下所示:
class Holder<T> { public T Value { get; set; } }
如果您愿意,可以添加隐式强制转换和构造函数。
您也可以通过公开基类中的Status
属性并在test.Status
中编写Main
来执行此操作。
你也有一个不太明显的问题
您的代码中没有任何内容强制它等待其他线程完成,因此您的Console.WriteLine
可能会在另一个线程分配字段之前运行。
答案 1 :(得分:0)
通常,您希望将结果存储在ConcreteTest
中,当任务完成时,请阅读ConcreteTest
中的值,不要尝试将其推回原始位置。如果你真的想要推回它,你可以使用回调和lambda这样做。
class ConcreteTest:ATest
{
public ConcreteTest(Action<string> statusResponder):base(statusResponder)
{
}
public override void PerformTest()
{
// Do some things...
// And some more...
statusResponder("changed");
}
}
class Program
{
static void Main(string[] args)
{
String isPassed = "original";
ATest test = new ConcreteTest(status => isPassed = status);
test.Start();
// Need to add a way to wait for task to be done here
Console.WriteLine(isPassed); // Prints out "original"
}
}