我是否误解了有关通过引用传递的内容?
SomeClass是IObserver实现者:
bool value = false;
provider.Subscribe(new SomeClass(ref value));
while (!value)
{
provider.GetEvents() //triggers OnNext
}
在SomeClass中:
public SomeClass(ref bool value)
{
this.value = value;
}
public void OnNext(object someUpdatedValue)
{
value = true;
}
值永远不会变为真,而while循环永远不会中断。怎么会?是否将SomeClass的value属性赋值给值的引用不会持久?
编辑:在看到前两个回复后,我的新问题是:
如何在不使用静态变量的情况下实现这种行为?
答案 0 :(得分:2)
按引用传递仅影响作为方法参数传递的变量。在您的示例中,false
的值,即value
分配给this.value
时包含的变量this.value
,将被复制到class VolatileBoolWrapper
{
public bool Value { get { return _value; } }
private volatile bool _value;
public void SetValue(bool value)
{
_value = value;
}
}
}字段。没什么。
C#中没有什么神奇的东西会记住其中来自哪个值,并且稍后更新变量,当分配了其值的字段稍后更改时。
将SomeClass的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
类充当呼叫者和被呼叫者的中间人。
<强>&LT;编辑&gt; 强>
请注意,我将该字段标记为Volatile...
,并将该类OnNext()
命名为安全。问题上没有足够的上下文让我知道“触发器”实际意味着什么(即同一个线程实际上是设置了值,还是涉及线程之间的交互)。
如果发生GetEvents()
的调用发生在同一个线程内,严格来说是对volatile
的调用,那么你可以在这里省略TaskCompletionSource
的使用(并忽略)或者至少打折,我关于下面的投票的说明)。
的&LT; /编辑&gt; 强>
所有这一切,坦率地说:对这样的变量进行轮询几乎总是错误的方法来实现自己的目标。对于像这样的事情总是有更好的方法,但在现代C#中,我会说await
是最好的选择。与之前的其他机制一样,它允许您的等待代码不再持续使用CPU时间检查以查看事件是否已发生;与它们不同,它还提供了一种很好的机制,允许整个线程继续执行,执行其他工作,只在text-overflow: ellipses
语句处恢复,当事件实际发生时,您等待事件
答案 1 :(得分:1)
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
是值类型,因此它的值被复制回来。当您更改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
,并摆脱这整个/ 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;
}
}
那里 - 那更简单。由于调用者有一个引用到我们的实例,它改变了它的状态(没有改变它的身份),我们不必担心所有该方法调用by-ref / by-val struct vs class mumbo-jumbo。