剛剛看到網友分享String.Join的小技巧,我就想到我前陣子,因為String.Join只能對String陣列操作,不能對集合如List、DataTable、IQueryable等類型操作,所以我自己就寫了一個Extension來方便自己,以便對集合可以是像String一樣使用Join。
使用方式
IEnumerable source = null;
source.Join();
source.Join("PropertyName",",");
source.Join(separator: "|");
原始碼
/// <summary>
/// 集合、陣列等反複物件的操作。
/// </summary>
public static class IterateExtension
{
/// <summary>
/// 將物件陳列組合成字串,使用物件的屬性名稱取得物件屬性值。
/// </summary>
/// <param name="source">可列舉來源</param>
/// <param name="propertyName">物件的屬性名稱</param>
/// <param name="separator">分隔字串</param>
/// <returns></returns>
public static string Join(this IEnumerable source, string propertyName = null, string separator = ",")
{
if (source == null)
{
return null;
}
bool nullProperty = propertyName == null;
StringBuilder sb = new StringBuilder();
PropertyDescriptor pi = null;
Type type = null;
foreach (object value in source)
{
if (value == null)
{
continue;
}
if (value is string || nullProperty)
{
//value是string或propertyName是空的直接輸出。
sb.Append(value);
}
else if (value is DataRow)
{
sb.Append(((DataRow)value)[propertyName]);
}
else
{
Type temp = value.GetType();
if (type != temp)
{
type = temp;
// 用TypeDescriptor.GetProperties(object)取屬性有實作ICustomTypeDescriptor,會取值自定屬性,如DataRowViw
// 用TypeDescriptor.GetProperties(type)取屬性不會判斷ICustomTypeDescriptor。
pi = TypeDescriptor.GetProperties(value)[propertyName];
if (pi == null)
{
throw new InvalidOperationException(string.Format("{0} Property {1} Not Exist.", type, propertyName));
}
}
sb.Append(pi.GetValue(value));
}
sb.Append(separator);
}
return (separator == null || sb.Length == 0) ? sb.ToString() : sb.ToString(0, sb.Length - separator.Length);
}
}
單元測試
public class Test
{
public string P1 { get; set; }
public int P2 { get; set; }
public override string ToString()
{
return this.P1;
}
}
[TestMethod()]
public void JoinTest()
{
IEnumerable source = null;
//String
source = new string[] { "A", "B", "C" };
Assert.AreEqual(source.Join(), "A,B,C");
Assert.AreEqual(source.Join(separator : "|"), "A|B|C");
//List + class
source = new List<Test> { new Test() { P1 = "A", P2 = 1 }, new Test() { P1 = "B", P2 = 2 }, new Test() { P1 = "C", P2 = 3 } };
Assert.AreEqual(source.Join(), "A,B,C");
Assert.AreEqual(source.Join("P2"), "1,2,3");
//DataTable And DataView
DataTable dt = new DataTable();
dt.Columns.Add("Col1");
dt.Columns.Add("Col2");
dt.Rows.Add("A", 1);
dt.Rows.Add("B", 2);
dt.Rows.Add("C", 3);
Assert.AreEqual(dt.Rows.Join("Col1"), "A,B,C");
Assert.AreEqual(dt.Rows.Join("Col2"), "1,2,3");
Assert.AreEqual(dt.DefaultView.Join("Col1"), "A,B,C");
Assert.AreEqual(dt.DefaultView.Join("Col2"), "1,2,3");
//IQueryable
TestDataContext db = new TestDataContext();
source = (from e in db.Employers
select e).Take(3);
Assert.AreEqual(source.Join("Name"), "A,B,C");
}
註:原本是在2008的,Method有多載,换到2010就小小的改寫成使用預設值。