提问者:小点点

为结构索引器赋值


我得到编译器错误“不能修改表达式,因为它不是变量”。我理解不能因为返回struct的副本而给struct属性赋值,但我认为这不适用于这种情况。

我有一个DualArray类,它的目的是使两个数组保持同步。我想使用A属性访问'first'数组中的项,使用B属性访问'second'数组中的项。这些属性将具有索引器的结构返回到各自的数组中。

public struct Accessor<T>
{
  private readonly T[] _array;
  public Accessor(T[] array)
  { _array = array; }
  public T this[int index]
  {
    get { return _array[index]; }
    set { _array[index] = value; }
  } 
}
/// <summary>
/// Maintains two arrays.
/// </summary>
public class DualArray<T1, T2>
{
  // ...
  public Accessor<T1> A
  {
    get { return new Accessor<T1>(_arrayT1); }
  }
  public Accessor<T2> B
  {
    get { return new Accessor<T2>(_arrayT2); }
  }
  // ...
}

现在,当我尝试使用这段代码时,我得到了一个错误:

DualArray<int, bool> dual = new DualArray<int, bool>();
// ...
dual.A[5] = 2; // <-- sad trombone.

如果我将访问器类型更改为class,一切都可以正常工作,但是即使复制了结构,对相关数组的引用仍然有效吗?或者这个例子中的错误仅仅是因为我做了一些我不应该做的事情,而不是一个真正的错误?


共2个答案

匿名用户

“我理解,不能因为返回的是结构的副本而给结构属性赋值,但我认为这不适用于这种情况。”

是的,在这种情况下,它不会导致正常情况下的bug.br>但是编译器不允许这样做,因为这种写到-a-copy-of-a-struct的做法很有可能导致bug。

错误消息包含解决方案:

无法修改DualaArray.a'的值类型返回值。考虑将值存储在临时变量中。

因此,将其存储在一个变量中:

var a = dual.A;
a[5] = 2;

匿名用户

您可以这样做:

DualArray<int, bool> dual = new DualArray<int, bool>();
var acc = dual.A;
acc[5] = 2;

最初的设计错误是由于直接更改value-Type(从方法返回)属性造成的,这可能会导致意外的结果,因为它引用了value-Type的副本。但是在您的例子中,由于您通过在类中存储用于构造结构的备份数组来混合引用类型中的值类型,因此不会得到意外的结果。

来自错误文档:

发生此错误是因为赋值时复制了值类型。当您从属性或索引器检索值类型时,您获取的是对象的副本,而不是对对象本身的引用。返回的副本不由属性或索引器存储,因为它们实际上是方法,而不是存储位置(变量)。必须先将副本存储到声明的变量中,然后才能修改它。

如果正在定义类或结构,则可以通过修改属性声明来解决此错误,以提供对结构成员的访问。如果正在编写客户端代码,则可以通过创建自己的结构实例,修改其字段,然后将整个结构赋回属性来解决错误。作为第三种选择,您可以将结构更改为类。