如何通过“引用”将其分配给c#中的类字段?

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何通过“引用”将其分配给c#中的类字段?相关的知识,希望对你有一定的参考价值。

我试图了解如何通过“引用”分配给c#中的类字段。

我有以下示例要考虑:

 public class X
 {

  public X()
  {

   string example = "X";

   new Y( ref example );

   new Z( ref example );

   System.Diagnostics.Debug.WriteLine( example );

  }

 }

 public class Y
 {

  public Y( ref string example )
  {
   example += " (Updated By Y)";
  }

 }

 public class Z
 {

  private string _Example;

  public Z( ref string example )
  {

   this._Example = example;

   this._Example += " (Updated By Z)";

  }

 }

 var x = new X();

运行上面的代码时,输​​出是:

X(由Y更新)

并不是:

X(由Y更新)(由Z更新)

正如我所希望的那样。

似乎为字段分配“ref参数”会丢失引用。

在分配字段时有没有办法保持引用?

谢谢。

答案

不,ref纯粹是一种召唤惯例。您无法使用它来限定字段。在Z中,_Example设置为传入的字符串引用的值。然后使用+ =为其分配新的字符串引用。你永远不会分配给例子,所以ref没有效果。

你想要的唯一解决方法是拥有一个共享的可变包装器对象(一个数组或一个假设的StringWrapper),它包含引用(这里是一个字符串)。通常,如果需要,可以为要共享的类找到更大的可变对象。

 public class StringWrapper
 {
   public string s;
   public StringWrapper(string s)
   {
     this.s = s;
   }

   public string ToString()
   {
     return s;
   }
 }

 public class X
 {
  public X()
  {
   StringWrapper example = new StringWrapper("X");
   new Z(example)
   System.Diagnostics.Debug.WriteLine( example );
  }
 }

 public class Z
 {
  private StringWrapper _Example;
  public Z( StringWrapper example )
  {
   this._Example = example;
   this._Example.s += " (Updated By Z)";
  }
 }
另一答案

正如其他人所指出的那样,你不能拥有“ref to variable”类型的字段。然而,只知道你不能做到这一点可能并不令人满意;你可能也想先知道,为什么不知道,其次,如何解决这个限制。

原因是因为只有三种可能性:

1)禁止ref类型的字段

2)允许ref类型的不安全字段

3)不要将临时存储池用于局部变量(也称为“堆栈”)

假设我们允许ref类型的字段。然后你可以做到

public ref int x;
void M()
{
    int y = 123;
    this.x = ref y;
}

现在你可以在M完成后访问。这意味着要么我们遇到(2) - 访问this.x会崩溃并死亡,因为y的存储不再存在 - 或者我们是在案例(3)中,并且本地y存储在垃圾上收集堆,而不是临时内存池。

我们喜欢将局部变量存储在临时池中的优化,即使它们是由ref传递的,我们也不喜欢你可能会留下定时炸弹,这可能会导致程序崩溃并在以后死亡。因此,选项一是:没有引用字段。

注意,对于作为匿名函数的封闭变量的局部变量,我们选择option(3);这些局部变量不会从临时池中分配出来。

这就引出了第二个问题:你如何解决这个问题?如果你想要一个ref字段的原因是制作另一个变量的getter和setter,那就完全合法了:

sealed class Ref<T>
{
    private readonly Func<T> getter;
    private readonly Action<T> setter;
    public Ref(Func<T> getter, Action<T> setter)
    {
        this.getter = getter;
        this.setter = setter;
    }
    public T Value { get { return getter(); } set { setter(value); } }
}
...
Ref<int> x;
void M()
{
    int y = 123;
    x = new Ref<int>(()=>y, z=>{y=z;});
    x.Value = 456;
    Console.WriteLine(y); // 456 -- setting x.Value changes y.
}

你去吧y存储在gc堆上,x是一个能够获取和设置y的对象。

请注意,CLR确实支持ref本地和ref返回方法,但C#不支持。也许C#的假设未来版本将支持这些功能;我有它的原型,它运作良好。但是,这在优先级列表中并不是很高,所以我不会抱有希望。

更新:上面段落中提到的功能最终在C#7中实现了。但是,您仍然无法在某个字段中存储引用。

另一答案

您忘记更新Z类中的引用:

public class Z {
    private string _Example;

    public Z(ref string example) {
        example = this._Example += " (Updated By Z)";
    }
}

输出:X(由Y更新)(由Z更新)

需要记住的是,字符串的+ =运算符调用String.Concat()方法。这会创建一个新的字符串对象,但不会更新字符串的值。 String对象是不可变的,字符串类没有任何允许您更改值的方法或字段。与常规引用类型的默认行为非常不同。

因此,如果使用字符串方法或运算符,则必须始终将返回值赋给变量。这是非常自然的语法,值类型的行为方式相同。如果使用int而不是字符串,则代码将非常相似。

以上是关于如何通过“引用”将其分配给c#中的类字段?的主要内容,如果未能解决你的问题,请参考以下文章

是否可以将默认引用分配给 Unity 中的非 MonoBehaviour 类

C++ 通过分配其他构造函数初始化的类成员来初始化类成员是一个坏主意吗?

PHP中的ArrayAccess——通过引用分配给偏移量

如何将从输入字段获取的画布名称与Javascript中的画布合并?

通过引用传递给构造函数

如何分解数组并将块分配给各个复选框字段?