C#中为何IEnumerable随XDocument变更自动更新而List不会
问题描述
现有XML文件
<products> <product id="1" name="USB Flash Memory 2 GB" /> <product id="2" name="USB Flash Memory 8 GB" /> <product id="3" name="Wireless Multi Media Keyboard" /> </products>
问题说明
使用XDocument读取该文件时,将查询结果存储在IEnumerable<Product>类型的变量中,向XDocument添加新元素时该变量会自动更新;改用List<Product>类型存储查询结果后则不会同步更新,询问二者差异的原因。
完整测试代码
namespace IEnumerableTest { public class Product { public int ID { get; set; } public string ProductName { get; set; } } } namespace IEnumerableTest { public partial class Form1 : Form { static string path = @"Products.xml"; XDocument xDoc = XDocument.Load(path); IEnumerable<Product> productsEnum; List<Product> productsList; public Form1() { InitializeComponent(); productsEnum = from p in xDoc.Descendants("product") select new Product { ID = int.Parse(p.Attribute("id").Value), ProductName = p.Attribute("name").Value }; productsList = (from p in xDoc.Descendants("product") select new Product { ID = int.Parse(p.Attribute("id").Value), ProductName = p.Attribute("name").Value }).ToList(); } void BindDataGridView() { dataGridViewEnum.DataSource = productsEnum.ToList(); dataGridViewList.DataSource = productsList; } private void Form1_Load(object sender, EventArgs e) { BindDataGridView(); } private void btnAdd_Click(object sender, EventArgs e) { XElement newProduct = new XElement("product", new XAttribute("id", 4), new XAttribute("name", "Wireless USB Optical Mouse")); xDoc.Root.Add(newProduct); BindDataGridView(); } } }
运行效果

解答
这个差异的核心原因是LINQ的延迟执行(惰性求值)特性:
- 对于
IEnumerable<Product>类型的productsEnum:你在构造函数中给它赋值时,仅仅是存储了LINQ查询的定义逻辑,并没有立刻遍历XDocument的节点生成Product对象。只有当你实际枚举这个IEnumerable的时候(也就是BindDataGridView方法中调用productsEnum.ToList()的时机),才会真正执行查询逻辑,从当前的XDocument中读取所有product节点生成对应实例。所以你给XDocument添加新节点后,下次执行查询自然能拿到最新的节点数据,看起来就像是变量自动更新了。 - 对于
List<Product>类型的productsList:你在构造函数中调用了ToList()方法,相当于强制立刻执行了LINQ查询,将当时XDocument中存在的product节点全部转换为Product实例,存入了List的内存空间中。此时这个List已经和原XDocument完全脱离关联,后续你修改XDocument的内容不会影响已经生成的List中的元素,自然就不会同步更新。
如果不需要IEnumerable随XDocument变更同步更新,只需要在给IEnumerable变量赋值时追加ToList()方法,提前执行查询缓存结果即可。
内容的提问来源于stack exchange,提问作者Taher
相关产品推荐
相关产品推荐

