• 语法糖


    先介绍一个例子,如下两个表格数据:

    左边表格
    ItemCode 属性值1 属性值2 属性值3
    A 20 30 40
    B 30 30 44
    C 25 30 44
    D 34 30 60
    右边表格
    ItemCode 分类 属性值1 属性值2 属性值3
    A 0 90 90 90
    B 1 90 90 90
    D 2 90 90 90
    E 2 90 90 90

    需求说明如下:

    1. 【分类】 列的值 有三种类型 0、1、2
    2. ItemCode是唯一的,在表格中不会重复
    3. 将左边的数据,按照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,
    
        }

    结论:

    1.   软件代码中的注释对代码维护工作非常重要。但是,结构良好,更接近于自然语言描述的代码更重要。甚至,有时候可以替代注释的效果。
    2. 软件需求在转换为软件代码过程中,仍然有值得仔细推敲,整理的必要,这样才能保证在实现过程中,尽量与软件需求描述一致。
    3. 当然,上述代码中,第二种方案有时候也未必是最优的。如果考虑性能的话,大数据量的dataTable,可能第一种方案还是可取的。
    4. 以上没有使用Linq去实现,如果用到Linq的话,代码应该还可以简化。
  • 相关阅读:
    C++访问WebService gSoap方式
    vc6
    POS 60域用法
    本次操作由于这台计算机的限制而被取消
    POS的一点杂笔
    QT5.5
    注册表常用快捷键
    WebBrowser与IE的关系,如何设置WebBrowser工作在IE9模式下?
    js中的prototype属性
    WPF入门教程系列二十——ListView示例(二)
  • 原文地址:https://www.cnblogs.com/dcll/p/1784698.html
Copyright © 2020-2023  润新知