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

时间:2015-07-06 22:24:38

标签: c# asp.net reference pass-by-reference

我是否误解了有关通过引用传递的内容?

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属性赋值给值的引用不会持久?

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

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

2 个答案:

答案 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。