我已经解决了这个问题,但我正在试图找出它的工作原理.基本上,我使用foreach循环遍历结构列表.如果我在调用struct的方法之前包含引用当前结构的LINQ语句,则该方法无法修改结构的成员.无论是否甚至调用LINQ语句,都会发生这种情况.我能够通过将我正在寻找的值分配给变量并在LINQ中使用它来解决这个问题,但我想知道是什么导致了这一点.这是我创建的一个例子.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace WeirdnessExample
{
public struct RawData
{
private int id;
public int ID
{
get{ return id;}
set { id = value; }
}
public void AssignID(int newID)
{
id = newID;
}
}
public class ProcessedData
{
public int ID { get; set; }
}
class Program
{
static void Main(string[] args)
{
List<ProcessedData> processedRecords = new List<ProcessedData>();
processedRecords.Add(new ProcessedData()
{
ID = 1
});
List<RawData> rawRecords = new List<RawData>(); …Run Code Online (Sandbox Code Playgroud) 这个问题真的是这个问题的一个分支,但我认为它应该得到自己的答案.
根据ECMA-334第15.13节(关于using声明,以下称为资源获取):
在资源获取中声明的局部变量 是只读的,并且应包括初始化器.如果嵌入语句试图修改这些局部变量(通过赋值或发生编译时间错误
++和--操作员)或它们传递作为ref或out参数.
这似乎解释了为什么下面的代码是非法的.
struct Mutable : IDisposable
{
public int Field;
public void SetField(int value) { Field = value; }
public void Dispose() { }
}
using (var m = new Mutable())
{
// This results in a compiler error.
m.Field = 10;
}
Run Code Online (Sandbox Code Playgroud)
但是这个怎么样?
using (var e = new Mutable())
{
// This is doing exactly the same thing, but …Run Code Online (Sandbox Code Playgroud) 要使用这样的初始化语法:
var contacts = new ContactList
{
{ "Dan", "dan.tao@email.com" },
{ "Eric", "ceo@google.com" }
};
Run Code Online (Sandbox Code Playgroud)
...我的理解是我的ContactList类型需要定义一个带有Add两个string参数的方法:
public void Add(string name, string email);
Run Code Online (Sandbox Code Playgroud)
对我来说有点困惑的是,在创建只读或固定大小的集合时,{ }初始化器语法似乎最有用.毕竟它意味着模仿数组的初始化语法,对吧?(好吧,所以数组不是只读的;但它们是固定大小的.)当然,它只能在编译时知道集合的内容(至少是元素的数量)时使用.
所以几乎看起来使用这个集合初始化器语法(有一个Add方法,因此是一个可变集合)的主要要求与它最有用的典型情况不一致.
我确信我没有像C#设计团队那样考虑这个问题; 看起来这个语法可能会有不同的规则,这些规则可以与其典型的使用场景进行更好的网格划分.
我离开基地吗?是否希望使用{ }语法来初始化固定大小的集合,这不像我想的那么常见?还有哪些其他因素可能会影响我对这种语法的要求的制定,而我根本就没有考虑过?
我对线程安全的方法参数的理解是:通过值传递到方法中的参数作为方法调用的参数中给出的数据的副本传递,因此它们对于该方法调用是唯一的,不能由任何其他任务更改。相反,参考参数可能会因在其他任务中运行的代码而易于更改。
话虽如此,我仍然不太清楚为什么下面的代码(不制作循环计数器的本地副本)在每个线程中返回相同的数字。
static void ExampleFunc(int i) =>
Console.WriteLine("task " + i);
Run Code Online (Sandbox Code Playgroud)
for (int i = 0; i < 10; i++)
{
int taskN = i; //local copy instead of i
Task.Run(() => Func(i));
}
Run Code Online (Sandbox Code Playgroud)
实际输出是:任务10十次,
我通过传递taskN而不是i来获得正确的输出(任务1到10)。
由于传递了类型值参数,因此我期望得到相同的结果。