C# 将值指定给结构索引器

C# 将值指定给结构索引器,c#,indexing,struct,C#,Indexing,Struct,我收到编译器错误“无法修改表达式,因为它不是变量”。我知道不能给struct属性赋值,因为返回了struct的副本,但我认为这不适用于这种情况 我有一个DualArray类,其目的是使两个数组保持同步。我想使用A属性访问“first”数组中的项,使用B属性访问“second”数组中的项。这些属性将具有索引器的结构返回到相应的数组中 public struct Accessor<T> { private readonly T[] _array; public Accessor(

我收到编译器错误“无法修改表达式,因为它不是变量”。我知道不能给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); }
  }
  // ...
}
公共结构访问器
{
专用只读T[]_数组;
公共访问器(T[]数组)
{{u数组=数组;}
公共T此[int索引]
{
获取{返回_数组[索引];}
集合{u数组[index]=value;}
} 
}
/// 
///维护两个阵列。
/// 
公共类双数组
{
// ...
公共存取器A
{
获取{返回新访问器(_arrayT1);}
}
公共存取器B
{
获取{返回新访问器(_arrayT2);}
}
// ...
}
现在,当我尝试使用此代码时,我得到了错误:

DualArray<int, bool> dual = new DualArray<int, bool>();
// ...
dual.A[5] = 2; // <-- sad trombone.
DualArray dual=新的DualArray();
// ...
dual.A[5]=2;// 您可以这样做:

DualArray<int, bool> dual = new DualArray<int, bool>();
var acc = dual.A;
acc[5] = 2;
DualArray dual=新的DualArray();
var acc=双A;
acc[5]=2;
原始设计错误是由于直接更改值类型(从方法返回)属性导致的,这可能会导致意外结果,因为这引用了值类型的副本。但是在你的例子中,由于你在引用类型中混合了值类型,通过在一个类中存储用来构造结构的后备数组,所以你不会得到意想不到的结果

从错误:

发生此错误的原因是赋值时复制了值类型。什么时候 如果从属性或索引器检索值类型,则 对象的副本,而不是对对象本身的引用。副本 属性或索引器不会存储返回的内容,因为它们 实际上是方法,而不是存储位置(变量)。你必须 将副本存储到您在修改之前声明的变量中 它

如果要定义类或结构,可以通过以下方法解决此错误: 修改属性声明以提供对成员的访问 结构的。如果您正在编写客户端代码,则可以解决该错误 通过创建自己的结构实例、修改其字段和 然后将整个结构重新分配给属性。作为第三个 或者,您可以将结构更改为类

“我理解不能为结构属性赋值 因为返回了一个结构的副本,但我认为这不适用 在这种情况下。”

是的,在这种情况下,它不会导致它通常导致的错误。
但是编译器根本不允许这样做,因为这种对结构的一个副本的写入有很大的可能导致错误

错误消息包含解决方案:

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

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

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

但是我的值类型上没有什么需要更改的,它将所有内容委托回它所包装的数组,数组是引用类型。@DavidRutten
dual.A
返回结构的副本。我确实注意到替代代码工作了,这很奇怪,因为我仍然认为,它做的事情完全相同。事实上,我已经测试过了,替代方案也能正常工作。@DavidRutten但它不会改变
dual
中的值。尝试复制代码<代码>var acc=双.A;acc[5]=2;var acc2=双A然后acc2保留原始值,而不是更新后的值,变为
2
是,它确实会改变。因为结构包含对数组的引用,并且当复制该结构时,新结构仍然包含对同一数组的引用。由于我的索引器直接使用该数组引用,所以当代码特别分布在两行中时,代码会按预期工作。谢谢,我认为这可能是一个伪装成错误的警告,因为这是一个非常严重的错误。遗憾的是,这个解决方案破坏了命名访问器的良好性。我现在将访问器更改为引用类型,这允许我保留简洁的赋值。@DavidRutten:这不是“伪装成错误的警告”-这是一个错误。表达式
dual.A
被分类为值,而不是变量。因此,您不能使用索引设置器。任何允许这样做的编译器都将违反语言规范。@JonSkeet,我明白了,这是一个错误,因为它是这样定义的,而不是因为它实际上是无意义的。公平地说,我理解为什么规范设计者认为将如此危险的代码扼杀在萌芽状态是很重要的。@DavidRutten:在几乎所有情况下,这都是荒谬的。这仅仅是因为您的值类型并不像大多数值类型所期望的那样(即通过“原件”修改“副本仍然可见”)。“表达式
dual.a
被归类为值,而不是变量。”这是我应该说的。