通过引用传递并存储对变量的引用 - 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 的引用不会持久化吗?

编辑:看到前两个回复后,我的新问题是:

如何在不使用静态变量的情况下实现这种行为?

按引用传递仅影响作为参数传递给方法的变量。在您的示例中,falsevalue,即您将其分配给 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 一样)。 structs 是值类型,因此它的 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.

的方法