通过引用传递并存储对变量的引用 - C#
Pass by reference and storing a reference to a variable - c#
我对引用传递有什么误解吗?
SomeClass 是 IObserver 实现者:
bool value = false;
provider.Subscribe(new SomeClass(ref value));
while (!value)
{
provider.GetEvents() //triggers OnNext
}
在某个类中:
public SomeClass(ref bool value)
{
this.value = value;
}
public void OnNext(object someUpdatedValue)
{
value = true;
}
值永远不会变为真,而 while 循环永远不会中断。怎么会?将 SomeClass 的值 属性 分配给 value 的引用不会持久化吗?
编辑:看到前两个回复后,我的新问题是:
如何在不使用静态变量的情况下实现这种行为?
按引用传递仅影响作为参数传递给方法的变量。在您的示例中,false
的 value,即您将其分配给 this.value
时包含的变量 value
,被复制到 this.value
字段。仅此而已。
C# 中没有什么神奇的东西可以记住 值来自哪里 并在稍后更改其值分配给的字段时更新变量。
Does assigning the value property of SomeClass to the reference of value not persist?
您没有分配 "the reference of value"。通过引用传递时发生的所有事情是,如果局部变量本身发生更改,那么传递的变量也会被修改。当您使用变量的值时,您只是在使用该值,而不是引用。
编辑:
没有更多的上下文,就不可能说出解决这个问题的最佳方法。但请注意,引用类型实现的效果与您似乎想要做的类似。例如:
class VolatileBoolWrapper
{
public bool Value { get { return _value; } }
private volatile bool _value;
public void SetValue(bool value)
{
_value = value;
}
}
VolatileBoolWrapper value = new VolatileBoolWrapper();
provider.Subscribe(new SomeClass(value));
while (!value.Value)
{
provider.GetEvents() //triggers OnNext
}
public SomeClass(VolatileBoolWrapper value)
{
this.value = value;
}
public void OnNext(object someUpdatedValue)
{
value.SetValue(true);
}
在这种情况下,VolatileBoolWrapper
class 充当调用者和被调用者之间的中间人。
<编辑>
请注意,为了安全起见,我将该字段标记为 volatile
,并将 class Volatile...
命名为。问题中没有足够的上下文让我知道 "triggers" 的实际含义(即同一个线程是否实际设置了值,或者这是涉及线程之间交互的东西)。
如果碰巧在同一个线程中调用OnNext()
,严格来说是通过调用GetEvents()
,那么你可以在这里省略volatile
的使用(并忽略,或者至少忽略我在下面关于投票的注释)。
综上所述,坦率地说:轮询像这样的变量几乎总是实现目标的错误方法。对于这样的事情总是有更好的方法,但在现代 C# 中,我会说 TaskCompletionSource
是最好的选择。与之前出现的其他机制一样,它允许您的等待代码不连续使用 CPU 时间检查以查看事件是否已发生;与它们不同,它还提供了一种出色的机制,允许 整个线程 继续执行,执行其他工作,仅在您等待事件的 await
语句处恢复事件实际发生。
ref
修饰符影响调用者,而不是被调用者。它允许您将调用者的变量重新分配给 "point to" 一个新值。例如:
bool myValue = true;
WithoutRef_YouCannotReassign(myValue);
Console.WriteLine(myValue); // myValue is still true
WithRef_YouCanReassign(ref myValue);
Console.WriteLine(myValue); // myValue is now false
void WithoutRef_YouCannotReassign(bool value) {
value = false;
}
void WithRef_YouCanReassign(bool value) {
value = false;
}
您正在尝试 输出 对 SomeClass.value
的引用。通常,只需交换您的分配就可以很好地工作(请记住,您正在更改调用者的变量以指向其他内容)
public SomeClass(ref bool value)
{
value = this.value;
}
但是,您遇到了另一个问题。由于 bool
是不可变的 - 即使您的调用者指向正确的值,您稍后通过覆盖它来将您自己的 value
指向其他值:
public void OnNext(object someUpdatedValue)
{
value = true; // value is now pointing somewhere else! the caller never got a reference to the somewhere else!
}
所以,现在,您实际上需要一个包装器来避免在将引用传递给它之后覆盖 SomeClass.value
:
struct MyBoolWrapper
{
public bool Value { get; set; }
}
public SomeClass(ref MyBoolWrapper value)
{
value = this.value;
}
public void OnNext(object someUpdatedValue)
{
value.Value = true;
}
现在,this 将不起作用,因为它是 struct
(就像 bool
一样)。 struct
s 是值类型,因此它的 value 被复制回来。当您更改 SomeClass.value
时,您将再次更改 不同的 副本! (这是我们倾向于使用不可变结构的原因之一,例如 bool
)。
所以,让我们将其更改为 class:
class MyBoolWrapper
{
public bool Value { get; set; }
}
这将按预期工作,因为您最终将 reference 传回给 MyBoolWrapper
(不会改变)。
所以,现在我们正在工作 - 但让我们看看一些清理工作。对于我们的调用者来说,必须 new
一个 MyBoolWrapper
只是为了我们可以将它指向其他东西,这似乎有点愚蠢。让我们改变一下:
MyBoolWrapper wrapper = null;
provider.Subscribe(new SomeClass(ref wrapper));
好吧,现在我们将其设置为 null
似乎很愚蠢。由于 SomeClass
提供了所有信息,我们就把它设为 out
(本质上,它与 ref
的工作原理相同,只是不需要初始化):
MyBoolWrapper wrapper;;
provider.Subscribe(new SomeClass(out wrapper));
当然,现在甚至不清楚为什么我们不能直接持有对 SomeClass
的引用,并摆脱整个 out/ref 舞蹈:
SomeClass someClass = new SomeClass();
provider.Subscribe(someClass);
while (!someClass.Value) {
provider.GetEvents();
}
class SomeClass {
public Value { get; private set; }
public void OnNext(object someUpdatedValue)
{
Value = true;
}
}
那里 - 这更简单。由于调用者对我们的实例有一个 reference,它改变了 它的状态(没有改变它的 identity),我们不必担心所有调用 by-ref/by-val struct 与 class mumbo-jumbo.
的方法
我对引用传递有什么误解吗?
SomeClass 是 IObserver 实现者:
bool value = false;
provider.Subscribe(new SomeClass(ref value));
while (!value)
{
provider.GetEvents() //triggers OnNext
}
在某个类中:
public SomeClass(ref bool value)
{
this.value = value;
}
public void OnNext(object someUpdatedValue)
{
value = true;
}
值永远不会变为真,而 while 循环永远不会中断。怎么会?将 SomeClass 的值 属性 分配给 value 的引用不会持久化吗?
编辑:看到前两个回复后,我的新问题是:
如何在不使用静态变量的情况下实现这种行为?
按引用传递仅影响作为参数传递给方法的变量。在您的示例中,false
的 value,即您将其分配给 this.value
时包含的变量 value
,被复制到 this.value
字段。仅此而已。
C# 中没有什么神奇的东西可以记住 值来自哪里 并在稍后更改其值分配给的字段时更新变量。
Does assigning the value property of SomeClass to the reference of value not persist?
您没有分配 "the reference of value"。通过引用传递时发生的所有事情是,如果局部变量本身发生更改,那么传递的变量也会被修改。当您使用变量的值时,您只是在使用该值,而不是引用。
编辑:
没有更多的上下文,就不可能说出解决这个问题的最佳方法。但请注意,引用类型实现的效果与您似乎想要做的类似。例如:
class VolatileBoolWrapper
{
public bool Value { get { return _value; } }
private volatile bool _value;
public void SetValue(bool value)
{
_value = value;
}
}
VolatileBoolWrapper value = new VolatileBoolWrapper();
provider.Subscribe(new SomeClass(value));
while (!value.Value)
{
provider.GetEvents() //triggers OnNext
}
public SomeClass(VolatileBoolWrapper value)
{
this.value = value;
}
public void OnNext(object someUpdatedValue)
{
value.SetValue(true);
}
在这种情况下,VolatileBoolWrapper
class 充当调用者和被调用者之间的中间人。
<编辑>
请注意,为了安全起见,我将该字段标记为 volatile
,并将 class Volatile...
命名为。问题中没有足够的上下文让我知道 "triggers" 的实际含义(即同一个线程是否实际设置了值,或者这是涉及线程之间交互的东西)。
如果碰巧在同一个线程中调用OnNext()
,严格来说是通过调用GetEvents()
,那么你可以在这里省略volatile
的使用(并忽略,或者至少忽略我在下面关于投票的注释)。
综上所述,坦率地说:轮询像这样的变量几乎总是实现目标的错误方法。对于这样的事情总是有更好的方法,但在现代 C# 中,我会说 TaskCompletionSource
是最好的选择。与之前出现的其他机制一样,它允许您的等待代码不连续使用 CPU 时间检查以查看事件是否已发生;与它们不同,它还提供了一种出色的机制,允许 整个线程 继续执行,执行其他工作,仅在您等待事件的 await
语句处恢复事件实际发生。
ref
修饰符影响调用者,而不是被调用者。它允许您将调用者的变量重新分配给 "point to" 一个新值。例如:
bool myValue = true;
WithoutRef_YouCannotReassign(myValue);
Console.WriteLine(myValue); // myValue is still true
WithRef_YouCanReassign(ref myValue);
Console.WriteLine(myValue); // myValue is now false
void WithoutRef_YouCannotReassign(bool value) {
value = false;
}
void WithRef_YouCanReassign(bool value) {
value = false;
}
您正在尝试 输出 对 SomeClass.value
的引用。通常,只需交换您的分配就可以很好地工作(请记住,您正在更改调用者的变量以指向其他内容)
public SomeClass(ref bool value)
{
value = this.value;
}
但是,您遇到了另一个问题。由于 bool
是不可变的 - 即使您的调用者指向正确的值,您稍后通过覆盖它来将您自己的 value
指向其他值:
public void OnNext(object someUpdatedValue)
{
value = true; // value is now pointing somewhere else! the caller never got a reference to the somewhere else!
}
所以,现在,您实际上需要一个包装器来避免在将引用传递给它之后覆盖 SomeClass.value
:
struct MyBoolWrapper
{
public bool Value { get; set; }
}
public SomeClass(ref MyBoolWrapper value)
{
value = this.value;
}
public void OnNext(object someUpdatedValue)
{
value.Value = true;
}
现在,this 将不起作用,因为它是 struct
(就像 bool
一样)。 struct
s 是值类型,因此它的 value 被复制回来。当您更改 SomeClass.value
时,您将再次更改 不同的 副本! (这是我们倾向于使用不可变结构的原因之一,例如 bool
)。
所以,让我们将其更改为 class:
class MyBoolWrapper
{
public bool Value { get; set; }
}
这将按预期工作,因为您最终将 reference 传回给 MyBoolWrapper
(不会改变)。
所以,现在我们正在工作 - 但让我们看看一些清理工作。对于我们的调用者来说,必须 new
一个 MyBoolWrapper
只是为了我们可以将它指向其他东西,这似乎有点愚蠢。让我们改变一下:
MyBoolWrapper wrapper = null;
provider.Subscribe(new SomeClass(ref wrapper));
好吧,现在我们将其设置为 null
似乎很愚蠢。由于 SomeClass
提供了所有信息,我们就把它设为 out
(本质上,它与 ref
的工作原理相同,只是不需要初始化):
MyBoolWrapper wrapper;;
provider.Subscribe(new SomeClass(out wrapper));
当然,现在甚至不清楚为什么我们不能直接持有对 SomeClass
的引用,并摆脱整个 out/ref 舞蹈:
SomeClass someClass = new SomeClass();
provider.Subscribe(someClass);
while (!someClass.Value) {
provider.GetEvents();
}
class SomeClass {
public Value { get; private set; }
public void OnNext(object someUpdatedValue)
{
Value = true;
}
}
那里 - 这更简单。由于调用者对我们的实例有一个 reference,它改变了 它的状态(没有改变它的 identity),我们不必担心所有调用 by-ref/by-val struct 与 class mumbo-jumbo.
的方法