【问题标题】:List Sorting and pattern-matching列表排序和模式匹配
【发布时间】:2011-09-01 10:28:22
【问题描述】:

我正在尝试将电报列表排序为奴隶列表。

如果 PrimeAddress 和 SecondaryAddress 匹配,则报文属于从站。

设备存储在数据表中。

我想检查设备是否已经包含电报。

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

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();
    }

此代码不能很好地工作,它不会检查设备是否已经包含电报。有更好的想法吗?

【问题讨论】:

  • 你应该在问题中提供表格结构
  • 好的,请稍等...
  • 一些 cmets 只是通过阅读: 1.) 不要对列使用那些讨厌的索引(3 = Object 等) - 即使像“Object”这样的魔术字符串也比这更好。 2.) 考虑使用类型化的表格 - 而不是你不需要 - 做你的问题:没有看到这些东西是什么,我肯定没有更好的主意......

标签: c# linq list pattern-matching


【解决方案1】:

您可以采取一些措施来优化您的代码。首先将所有不会改变的东西从内部循环中取出。例如。您使用 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();
        }
    }

【讨论】:

  • 您的代码工作正常,但如果我运行两次函数,它不能修复双重输入。任何想法如何解决这个问题?也许.contains?
  • 取决于您在谈论重复时的意思。它不会(至少不应该)两次添加相同的对象,但可能会添加两个具有相同值的对象。如果你让我知道它是引用平等还是价值平等。如果是后者,您需要进行一些比较,但我可以展示如何使用它
  • 这是价值平等,这就是为什么 conatins 不起作用的问题。每个 MbusTelegram 都有时间戳。所以我必须检查设备列表中是否已经存在具有相同时间戳的电报。我不确定谁来实现这一点,也许一开始就删除所有这些?
  • 可能是这样的:var oldTime = from m in mList where m.TimeStamp == ?? list.RemoveRange(oldTime);
猜你喜欢
  • 2016-05-10
  • 1970-01-01
  • 2019-02-11
  • 2013-11-01
  • 2023-04-02
  • 2015-12-03
  • 2016-04-25
  • 1970-01-01
  • 2015-01-17
相关资源
最近更新 更多