传递引用并存储对变量-c#的引用
本文关键字:引用 变量 -c# 存储 | 更新日期: 2023-09-27 18:01:14
我是否误解了传递引用?
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;
}
值永远不会变为true,while循环永远不会中断。为什么?将SomeClass的value属性分配给value的引用是否不持久?
编辑:在看到前两个回答后,我的新问题是:
在不使用静态变量的情况下,我如何实现这种行为?
通过引用只影响作为参数传递给方法的变量。在您的示例中,false
的值将复制到this.value
字段,该值是将变量value
分配给this.value
时包含的值。没什么了。
C#中没有什么神奇的东西会记住该值来自哪里,并在稍后更改其值所分配的字段时更新变量。
将SomeClass的value属性分配给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
类充当调用者和被调用者的中间人。
<编辑>
注意,为了安全起见,我将字段标记为volatile
,并将类命名为Volatile...
。这个问题没有足够的上下文让我知道"触发器"的实际含义(即,同一个线程是否真的设置了值,或者这是否涉及线程之间的交互)。
如果对OnNext()
的调用恰好发生在同一个线程内,严格地说是由于对GetEvents()
的调用,那么您可以在这里省略对volatile
的使用(并忽略,或者至少忽略我下面关于轮询的注意事项)。
<编辑>
坦率地说,所有这些都表明:对这样的变量进行民意调查几乎总是实现目标的错误方式。对于这样的事情,总是有更好的方法,但在现代C#中,我想说TaskCompletionSource
是最好的选择。与之前的其他机制一样,它允许等待代码不连续使用CPU时间检查来查看事件是否发生;与它们不同的是,它还提供了一种极好的机制,允许整个线程继续执行,执行其他工作,只在事件实际发生时在等待事件的await
语句处恢复。
ref
修饰符影响调用者,而不是被调用者。它允许您将调用方的变量重新分配为"指向"一个新值。例如:
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;
}
现在,这个不起作用,因为它是struct
(就像bool
一样)。struct
s是值类型,因此它的值会被复制回来。当您更改SomeClass.value
时,您将再次更改不同的副本!(这就是我们倾向于喜欢不可变结构的原因之一,比如bool
)。
所以,让我们把它改成一个类:
class MyBoolWrapper
{
public bool Value { get; set; }
}
这将按预期工作,因为您最终会将引用传递回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;
}
}
那里——那更简单。由于调用方对我们的实例有一个引用,该引用更改了的状态(不更改其标识),因此我们不必担心所有由ref/by-val struct与class mumbo jumbo调用的方法。