[C#]IEnumerable型態回傳注意事項

自Linq出來以後,個人的程式撰寫習慣又因此有了些許的改變,變得習慣會盡量用IEnumerable型態去傳遞集合的資料,因為透過這樣的型態可以將集合真正的型態隱含在背後,若某天有需求要替換集合類型,動到的部份會比較少,也可以實現像Linq一樣具延遲載入的效果。


在前陣子開發時才發現,這樣的做法會隱藏著一些必需注意到的細節。像是使用Linq查詢:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;

namespace ConsoleApplication20
{
class Program
{
static void Main(string[] args)
{
IEnumerable<FileInfo> allDatas = GetDatas(@”c:\”);

foreach (FileInfo item in allDatas)
{
item.Name = “New FileName”;
}

foreach (FileInfo item in allDatas)
{
Console.WriteLine(item.Name);
}
}
public static IEnumerable<FileInfo> GetDatas(String path)
{
var linq = from file in Directory.GetFiles(path)
select new FileInfo() { Name = Path.GetFileName(file) };
return linq;
}

}

class FileInfo
{
private string _name;
public string Name
{
get { return _name; }
set
{
if (_name != value)
{
_name = value;
}
}
}
}
}


或是透過yield實現IEnumerable型態的回傳時

...
public static IEnumerable<FileInfo> GetDatas(String path)
{
foreach (var file in Directory.GetFiles(path))
{
yield return new FileInfo() { Name = Path.GetFileName(file) };
}
}
...

我們都可以注意到ㄧ開始從GetDatas取得的物件實體跟後續透過臨時變數取得的都不同…

image

必須要在取得IEnumerable型態的資料時透過ToArray、或是ToList之類的方法先行處理過,或是盡量避免使用Linq與yield去獲取IEnumerable型態的資料後直接回傳:

...
IEnumerable<FileInfo> allDatas = GetDatas(@"c:\").ToArray();
...

image

目前這問題尚無確切的定論,個人暫時姑且推測應該是因為yield的特性所導致的效果。