在Java中,我们可以做类似的事情
final string str = "I am a string";
Thread thread = new Thread(...{
string str2 = "bla bla bla " + str;
});
我不完全明白该怎么做,但关键是我需要将 str 声明为 final,以便在我的线程中访问它,这样就不会有可变的损坏。
但是现在我该如何在 C# 中做到这一点
string str = "I am a string";
Task.Run(() => {
string str2 = "bla bla " + str;
});
它确实有效,但我不确定这是正确的方法。我正在使用Visual Studio for Xamarin。
让我们退后一步。
在 C# 中创建 lambda 时,将外部变量捕获为变量:
int x = 123;
Action change = () => { Console.WriteLine(x); x = 345; };
Console.WriteLine(x); // 123
change(); // 123
Console.WriteLine(x); // 345
change(); // 345
x = 789;
change(); // 789
Console.WriteLine(x); // 345
只有一个变量 x,它由 lambda 和方法的激活共享。
这非常有用,但在某些情况下可能会变得非常混乱:
List<Action> actions = new List<Action>();
for(int x = 0; x < 10; ++x)
{
actions.Add(() => { Console.WriteLine(x); });
}
actions[0](); // 10, surprise!
只有一个变量 x,所有 lambda 都共享它。
有些人想要第一种行为——lambda共享变量——但编写第二种程序的人想要第二种行为。 他们希望 lambda 捕获变量的当前值,而不是变量本身。
C# 只需选择第一个选项即可解决此问题。 Java通过使第一种场景非法化来解决这个问题。 如果您只能捕获永不更改的变量,那么您既没有将变量捕获为变量的好处,也没有问题。
我个人更喜欢 C# 选择,尽管它有缺点。但两者都是合理的、合理的和有原则的。不同的语言设计师会做出不同的选择。
该功能本身与线程安全无关。 (尽管它有线程安全方面;在 C# 中通过 lambda 在线程之间共享的变量不是易失性的。相反,这是一个关于变量的 lambda 捕获如何工作的设计决策。
完全明白该怎么做,但关键是我需要将 str 声明为 final才能在我的线程中访问它,所以没有 可变的腐败。
不,关键是线程声明包含一个Java闭包,这就是Java闭包的工作方式,即它们只能使用不可变(final
)对象创建。C# 中不存在此问题。
C# 中final
的等效项是关键字readonly
如果它像您正在做的那样用于字段,或者sealed
是否用于类或方法。
对于要在另一个线程中使用的变量,使用readonly
并不是严格要求的,您的代码现在将按原样工作,但它确实可以确保您不会意外地犯错误,这些错误可能导致不一致的行为,因为您更改了其他线程上的str
值。