列表排序和模式匹配

时间:2011-09-01 10:28:22

标签: c# linq list pattern-matching

我正在尝试将电报列表排序到列表中。

如果PrimeAddress和SecondaryAddress匹配,则电报属于Slave。

设备存储在Datatable中。

我想检查deivce是否已包含telegramm。

我的第一次尝试看起来像这样:

public static DataTable mdlform_NewMBUStele(int LoggerID, List<MbusTelegram> mList, DataTable _deviceDataTable)
    {
        //TODO Das ist total dirty und gar nicht clean hier...
        foreach (DataRow dRow in _deviceDataTable.Rows)
        {
            if (dRow.ItemArray[3] is Slave)
            {
                foreach (MbusTelegram mb in mList)
                {
                    int primeID = (int)dRow.ItemArray[1];
                    if (primeID == LoggerID)
                    {
                        Slave slv = (Slave)dRow.ItemArray[3];
                        foreach (MbusTelegram mbus in mList)
                        {
                            if (slv.PrimeAddress == mbus.Header.PrimeAddress && slv.SecondaryAdd == mbus.FixedDataHeader.SecondaryAddress)
                            {
                                if (slv.ListOfTelegramms == null)
                                {
                                    slv.ListOfTelegramms = new List<MbusTelegram>();
                                }
                                if (!slv.ListOfTelegramms.Contains(mbus))
                                {
                                    slv.ListOfTelegramms.Add(mbus);
                                    //TODO Check if the slave already contains the telegramm, if so don't add it..
                                }
                            }
                        }
                    }
                }
            }
        }
        return _deviceDataTable;
    }

数据表的结构:

private void IniDataTable()
    {
        _deviceDataTable = new DataTable("Table");
        _deviceDataTable.Columns.Add("ID", typeof(int));
        _deviceDataTable.Columns.Add("IDParent", typeof(int));
        _deviceDataTable.Columns.Add("Name", typeof(string));
        _deviceDataTable.Columns.Add("Object", typeof(object));
        _deviceDataTable.Rows.Add(new object[] { 0, DBNull.Value, "Addressen", null });
        //GenerateDummyDataTable();
        IniDeviceTreeView();
    }

此代码不能很好地工作,并且不检查设备是否已包含telegramm。有更好的想法吗?

1 个答案:

答案 0 :(得分:2)

您可以采取一些措施来优化代码。首先从内循环中取出所有不会改变的东西。例如。您使用ItemArray的每种类型。它们对于内循环的每次迭代都没有改变,但是随着外循环的每次迭代,你似乎在相同的集合上迭代两次,从不使用外循环的变量(mb),因此你可以完全消除该循环。我在下面的代码中都做过了。我还将内部循环转换为LINQ,但这主要是因为我发现它更容易阅读。我不确定Distinct是否解决了TODO。我不确定我会得到TODO。它可能并且可能无法解决,您需要验证

public static DataTable mdlform_NewMBUStele(int LoggerID, List<MbusTelegram> mList, DataTable _deviceDataTable){
            //TODO Das ist total dirty und gar nicht clean hier...
            foreach (DataRow dRow in _deviceDataTable.Rows.Cast<DataRow>().Where(d=>d.ItemArray[3] is Slave)){
                    var primeID = (int) dRow.ItemArray[1];
                    var slv = (Slave) dRow.ItemArray[3];
                    if (slv.ListOfTelegramms == null){
                        slv.ListOfTelegramms = new List<MbusTelegram>();
                    }
                    var list = slv.ListOfTelegramms;
                    if (primeID == LoggerID){
                        var items = from m in mList
                                    where
                                        slv.PrimeAddress == m.Header.PrimeAddress &&
                                        slv.SecondaryAdd == m.FixedDataHeader.SecondaryAddress && !list.Contains(m, MbusTelegramEqualityComparer.Default)
                                    select m;
                        list.AddRange(items.Distinct(MbusTelegramEqualityComparer.Default));
                        }
                    }
            return _deviceDataTable;
        }

如果你还想LINQify它的可读性,你可以这样做(这可能会稍微恶化):

public static DataTable mdlform_NewMBUStele2(int LoggerID, List<MbusTelegram> mList, DataTable _deviceDataTable){
            var pairs = from dRow in _deviceDataTable.Rows.Cast<DataRow>()
                        where dRow.ItemArray[3] is Slave
                        let primeID = (int) dRow.ItemArray[1]
                        let slv = (Slave) dRow.ItemArray[3]
                        let list = slv.ListOfTelegramms
                        where primeID == LoggerID
                        select
                            new{
                                   list,
                                   items = (from m in mList
                                            where
                                                slv.PrimeAddress == m.Header.PrimeAddress &&
                                                slv.SecondaryAdd == m.FixedDataHeader.SecondaryAddress &&
                                                !list.Contains(m, MbusTelegramEqualityComparer.Default)
                                            select m).Distinct(MbusTelegramEqualityComparer.Default)
                               };
            foreach (var pair in pairs){
                pair.list.AddRange(pair.items);
            }
            return _deviceDataTable;
        }

后一个示例要求将ListOfTelegrams初始化为空列表而不是null

修改 使用IEqualityComparer进行值比较。以下使用时间戳仅用于相等。代码随使用情况更新。如果ListOfTelegrams可以包含重复项,则您需要在对distinct的调用中处理此问题,否则您可以将调用保留为Distinct。

public class MbusTelegramEqualityComparer : IEqualityComparer<MbusTelegram>{
        public static readonly MbusTelegramEqualityComparer Default = new MbusTelegramEqualityComparer();
        public bool Equals(MbusTelegram x, MbusTelegram y){
            return x.TimeStamp == y.TimeStamp;
        }

        public int GetHashCode(MbusTelegram obj){
            return obj.TimeStamp.GetHashCode();
        }
    }