传递引用并存储对变量-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的引用是否不持久?

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

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

传递引用并存储对变量-c#的引用

通过引用只影响作为参数传递给方法的变量。在您的示例中,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(不会更改)。

所以,现在我们正在工作——但让我们看看一些清理工作。对于我们的呼叫者来说,必须使用newMyBoolWrapper,这样我们就可以将其指向其他内容,这似乎有点傻。让我们改变一下:

  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调用的方法。