语法糖
先介绍一个例子,如下两个表格数据:
左边表格
|
右边表格
|
需求说明如下:
- 【分类】 列的值 有三种类型 0、1、2
- ItemCode是唯一的,在表格中不会重复
- 将左边的数据,按照ItemCode,复制到右边表格
如果左右两边都有ItemCode,检查分类值,如果对应的分类值是0:则不复制。
如果分类值是1:则修改属性值1
如果分类值是2:则修改属性值 1- 3
如果仅在右边的表格没有的数据,则把分类值修改成0,属性值不修改
如果仅在左边的表格有的数据,则右边新增一条,分类值是2,属性值1-3复制过来
如上例子,复制后的结果如下:
| ItemCode | 分类 | 属性值1 | 属性值2 | 属性值3 |
| A(不变) | 0 | 90 | 90 | 90 |
| B(修改1) | 1 | 30 | 90 | 90 |
| D(修改1-3) | 2 | 34 | 30 | 60 |
| E(仅右边) | 0 | 90 | 90 | 90 |
| C(仅左边) | 2 | 25 | 30 | 44 |
针对上述要求,程序实现可能如下:
public void CopyData(DataTable leftTable, DataTable rightTable)
{
//找出在仅在右边存在或者都存在的数据
foreach (DataRow row in rightTable.Rows)
{
string itemCode = row["ItemCode"] as string;
DataRow[] rows = leftTable.Select("ItemCode = '" + itemCode + "'");
if (rows.Length == 0)
{
//仅修改分类值
row["分类"] = 0;
}
else
{
//左右都存在
int type = (int)row["分类"];
if (type > 0)
{
row["属性值1"] = row["属性值1"];
if (type == 2)
{
row["属性值2"] = row["属性值2"];
row["属性值3"] = row["属性值3"];
}
}
}
}
//找出仅在左边存在数据
foreach (DataRow row in leftTable.Rows)
{
string itemCode = row["ItemCode"] as string;
DataRow[] rows = rightTable.Select("ItemCode = '" + itemCode + "'");
if (rows.Length == 0)
{
//在右边新增一条
DataRow newRow = rightTable.NewRow();
newRow["分类"] = 2; //分类值是2
newRow["ItemCode"] = itemCode;
newRow["属性值1"] = row["属性值1"];
newRow["属性值2"] = row["属性值2"];
newRow["属性值3"] = row["属性值3"];
rightTable.Rows.Add(newRow);
}
else
{
//左右都存在
//什么也不做
}
}
}
上面的代码应该算是清晰的。而且有了足够的注释,不算很难读懂。
但是,这个代码和业务逻辑描述的是一致的吗?换句话说:如果没有需求文档,单单看上面代码,能得到上面的总结的需求吗?
我觉得要整理出上面需求比较难,理由如下。
- 从代码中看不出来分类值 是 仅有0、1、2
- 如果没有这些注释,也很难读出需求三种描述的逻辑(两者都有、仅左边有、仅右边有)。尤其两个循环存在,让代码逻辑更加不清晰。
针对第一个问题,似乎很好解决。可以定义一个枚举类型来描述分类值就可以了,如下:
public enum Enu分类
{
不改变 = 0,
仅改变1 = 1,
全部改变 = 2
}
相应的代码可以修改成这样
if (rows.Length == 0)
{
//仅修改分类值
row["分类"] =(int) Enu分类.不改变;
}
else
{
//左右都存在
Enu分类 type = (Enu分类)row["分类"];
switch(type)
{
case Enu分类.全部改变:
row["属性值1"] = row["属性值1"];
row["属性值2"] = row["属性值2"];
row["属性值3"] = row["属性值3"];
break;
case Enu分类.仅改变1:
row["属性值1"] = row["属性值1"];
break;
case Enu分类.不改变:
break;
}
}
但是,对于第2个问题,则比较复杂了。如果和能像需求说明那样简洁的实现呢?
仔细分析,实际上需求说明中表述两个两层意思:
1、对比左右两个表格,得到都存在的、仅左边、仅右边结果2、三种数据处理方式如下:1)、都存在的处理方式按照分类2)、仅左边的,则在右边新增一行3)、仅右边的,则修改分类为0
那么另外一种实现方式是
1、做一个DataTable对比类,得到三种类型数据
2、遍历这三种数据即可。
实现方式如下:
public void CopyData2(DataTable leftTable, DataTable rightTable)
{
DataTableComparer cmp = new DataTableComparer(leftTable, rightTable, "ItemCode");
foreach(CompareResult result in cmp.Rows)
{
switch (result.Type)
{
case ResultType.Both:
//左右都存在
Enu分类 type = (Enu分类)result.RightRow["分类"];
switch (type)
{
case Enu分类.全部改变:
result.RightRow["属性值1"] = result.LeftRow["属性值1"];
result.RightRow["属性值2"] = result.LeftRow["属性值2"];
result.RightRow["属性值3"] = result.LeftRow["属性值3"];
break;
case Enu分类.仅改变1:
result.RightRow["属性值1"] = result.LeftRow["属性值1"];
break;
case Enu分类.不改变:
break;
}
break;
case ResultType.LeftOnly:
DataRow newRow = rightTable.NewRow();
newRow["分类"] = Enu分类.全部改变;
newRow["ItemCode"] = result.LeftRow["ItemCode"];
newRow["属性值1"] = result.LeftRow["属性值1"];
newRow["属性值2"] = result.LeftRow["属性值2"];
newRow["属性值3"] = result.LeftRow["属性值3"];
rightTable.Rows.Add(newRow);
break;
case ResultType.RightOnly:
//仅修改分类值
result.RightRow["分类"] = (int)Enu分类.不改变;
break;
}
}
}
TableComparer类实现如下:
class DataTableComparer
{
public DataTableComparer(DataTable left, DataTable right, params string[] keys)
{
m_rows = CreateAllRows(left.Rows, right.Rows, keys);
}
private string GetKey(DataRow row, string[] keys)
{
StringBuilder sb = new StringBuilder();
foreach (string key in keys)
{
sb.Append(Convert.ToString(row[key]) + "|");
}
return sb.ToString();
}
private Dictionary<string, CompareResult> CreateAllRows(IEnumerable leftRows, IEnumerable rightRows, string[] keys)
{
Dictionary<string, CompareResult> rows = new Dictionary<string, CompareResult>();
foreach (DataRow row in leftRows)
{
string key = GetKey(row, keys);
CompareResult result = new CompareResult();
result.LeftRow = row;
result.Type = ResultType.LeftOnly;
rows.Add(key, result);
}
foreach (DataRow row in rightRows)
{
string key = GetKey(row, keys);
if (rows.ContainsKey(key))
{
CompareResult result = rows[key];
result.Type = ResultType.Both;
result.RightRow = row;
}
else
{
CompareResult result = new CompareResult();
result.RightRow = row;
result.Type = ResultType.RightOnly;
rows.Add(key, result);
}
}
return rows;
}
private Dictionary<string, CompareResult> m_rows;
public IEnumerable<CompareResult> Rows
{
get
{
foreach (KeyValuePair<string, CompareResult> keyValue in m_rows)
{
yield return keyValue.Value;
}
}
}
}
public class CompareResult
{
public ResultType Type;
public DataRow LeftRow;
public DataRow RightRow;
}
public enum ResultType
{
Both,
LeftOnly,
RightOnly,
}
结论:
- 软件代码中的注释对代码维护工作非常重要。但是,结构良好,更接近于自然语言描述的代码更重要。甚至,有时候可以替代注释的效果。
- 软件需求在转换为软件代码过程中,仍然有值得仔细推敲,整理的必要,这样才能保证在实现过程中,尽量与软件需求描述一致。
- 当然,上述代码中,第二种方案有时候也未必是最优的。如果考虑性能的话,大数据量的dataTable,可能第一种方案还是可取的。
- 以上没有使用Linq去实现,如果用到Linq的话,代码应该还可以简化。

浙公网安备 33010602011771号