none
困绕很久的C# Dictionary<T,T> contains问题 RRS feed

  • 问题

  • 我有一个实体类:

    public Dictionary<Model_Links, int> usedLinks = new Dictionary<Model_Links, int>();

    然后Model_links的定义是

    public partial class Model_Links
    {
    public int id {get;set;}
    public string title {get;set;}
    public string body {get;set;}
    }

    需求:我想判断里面是否有这个元素Model_Links onelink=new Model_Links();

    用usedLinks.containskey(onelink)来判断元素,一直返回的是false不存在

    我知道应该是Model_Links是对象不能直接搜索的原因。但是不知道如何来修改这段代码。官方去找到一个

    IEqualityComparer的比较器。但是不知道如何使用。

    求大牛给个完整的解决方案,能在Dictionay中,搜索泛型或者自定义的Model实体。

    同样List<Model_Links> lists也遇到了这个问题,困绕了我很久,

    一直找不到好的方法,

    跪求解答。非常感谢。

    我知道要重写IEquityComparer,但是不知道具体如何操作。新手呀。求代码。
    2012年12月17日 3:34

答案

  • dear

    我想你是要搜寻 Dictionary 集合里的Key,他是对象 Model_links,要比較对象要自己动手处理,不能直接拿來用,Model_links 里有许多的 property,你的搜寻条件应该是Model_links 里的 property,否则 CLR 怎知道您的比对条件为何,Dictionary 集合里的 Key 通常都是摆放实值型别(int,long) 或 string,你这样做只是找自己麻烦,两个对象的比较 http://msdn.microsoft.com/zh-cn/library/dd183752.aspx

    List 集合同样也是要比对 Model_links 里的 property

    private void button1_Click(object sender, EventArgs e)
    {
        Model_Links expect = new Model_Links() { id = 4, title = "title4", body = "boby4" };
    
        List<Model_Links> targetList = new List<Model_Links>()
            {
                new Model_Links() { id = 1, title = "title1", body = "boby1" },
                new Model_Links() { id = 2, title = "title2", body = "boby2" },
                new Model_Links() { id = 3, title = "title3", body = "boby3" },
                new Model_Links() { id = 4, title = "title4", body = "boby4" }
            };
    
        var query = (from model in targetList
                     where model.id == expect.id
                     select model).FirstOrDefault();
    
        if (query != null)
        {
            MessageBox.Show(string.Format("Found ID :{0}", query.id));
        }
    }


    你也可以利用序列化,将对象转成bytes,然后再比较两者的byte顺序,参考以下代码及link

    http://www.dotblogs.com.tw/yc421206/archive/2012/05/25/72390.aspx

    private void button2_Click(object sender, EventArgs e)
    {
        byte[] test1 = null;
        byte[] test2 = null;
        Model_Links expect = new Model_Links() { id = 3, title = "title3", body = "boby3" };
    
        List<Model_Links> targetList = new List<Model_Links>()
            {
                new Model_Links() { id = 1, title = "title1", body = "boby1" },
                new Model_Links() { id = 2, title = "title2", body = "boby2" },
                new Model_Links() { id = 3, title = "title3", body = "boby3" },
                new Model_Links() { id = 4, title = "title4", body = "boby4" }
            };
    
        using (MemoryStream expectMemory = new MemoryStream())
        {
            IFormatter formatter = new BinaryFormatter();
            formatter.Serialize(expectMemory, expect);
            expectMemory.Seek(0, SeekOrigin.Begin);
            test1 = expectMemory.ToArray();
        }
    
        foreach (var model in targetList)
        {
            using (MemoryStream expectMemory = new MemoryStream())
            {
                IFormatter formatter = new BinaryFormatter();
                formatter.Serialize(expectMemory, model);
                expectMemory.Seek(0, SeekOrigin.Begin);
                test2 = expectMemory.ToArray();
            }
            bool areEqual = test1.SequenceEqual(test2);
            if (areEqual)
            {
                break;
            }
        }
    }

    上面的代码看起来很繁杂,但也是个人目前认为最有效的方法之一。

    或是参考以下

    http://msdn.microsoft.com/zh-tw/library/dd183755.aspx

    http://msdn.microsoft.com/zh-tw/library/system.collections.iequalitycomparer(v=vs.80).aspx[补充]

    想说明的是:类的比较不能直接比较,我建议你可以仿造我的List写法使用LINQ查询手段去找寻是否存在一个你已经定义的实体(尝试使用Dictionary的Keys属性+LINQ等语句进行比较)。


    秘訣無它,唯勤而已 http://www.dotblogs.com.tw/yc421206/



    2012年12月17日 7:25
  • @我知道要重写IEquityComparer,但是不知道具体如何操作。新手呀。求代码。

    namespace CSharp
    {
        using System.Threading.Tasks;
        using System.Xml.Linq;
        using System.Linq;
        using System.Data;
        using System.Net;
        using System.Text;
        using System.IO;
        using System;
        using System.Collections.Generic;
        using System.Linq.Expressions;
     
        public partial class Model_Links
        {
            public int id { getset; }
            public string title { getset; }
            public string body { getset; }
        }
     
        public class MyComparer:IEqualityComparer<KeyValuePair<Model_Linksint>>
        {
            public bool Equals(KeyValuePair<Model_Linksint> x, KeyValuePair<Model_Linksint> y)
            {
                return x.Key.id == y.Key.id && x.Key.title == y.Key.title && x.Key.body == y.Key.body;
            }
     
            public int GetHashCode(KeyValuePair<Model_Linksint> obj)
            {
                return obj.Key.id.GetHashCode() ^ obj.Key.title.GetHashCode() ^ obj.Key.body.GetHashCode();
            }
        }
     
        class Program
        {
            static void Main(string[] args)
            {
                Dictionary<Model_Linksint> usedLinks = new Dictionary<Model_Linksint>();
                usedLinks.Add(new Model_Links() { id = 1, title = "title1", body = "boby1" }, 1);
                usedLinks.Add(new Model_Links() { id = 2, title = "title2", body = "boby2" }, 2);
                usedLinks.Add(new Model_Links() { id = 3, title = "title3", body = "boby3" }, 3);
                usedLinks.Add(new Model_Links() { id = 4, title = "title4", body = "boby4" }, 4);
     
                //对比的类
                KeyValuePair<Model_Linksint> m = new KeyValuePair<Model_Linksint>(new Model_Links { id = 1, title = "title1", body = "boby1" }, 1);
     
                //测试是否存在
                Console.WriteLine(usedLinks.Contains(m,new MyComparer()));
            }
        }
    }

    我的博客园
    慈善点击,点击此处
    和谐拯救危机,全集下载,净化人心

    2012年12月17日 8:09
    版主
  • dear

    基本上是不建议使用class 来当 key,不过还是希望透过讨论能带出更多的基础观念

    你可参考以下了解 Equals 的用法以及 GetHashCode 的功用
    http://www.dotblogs.com.tw/yc421206/archive/2012/12/17/85732.aspx
    http://www.dotblogs.com.tw/larrynung/archive/2009/12/16/12489.aspx

    只要是类方法带有前缀 Contains 关键字,都会用到 GetHashCode,CLR 是用 HashCode(杂凑) 来产判断集合是否存在某个物件。比如 Dictionary.ContainsKey / AddList.Contains,那 Dictionary.ContainsValue 有没有用到 HashCode?就留给您当研旧题目了。


    秘訣無它,唯勤而已 http://www.dotblogs.com.tw/yc421206/


    2012年12月19日 16:44

全部回复

  • dear

    我想你是要搜寻 Dictionary 集合里的Key,他是对象 Model_links,要比較对象要自己动手处理,不能直接拿來用,Model_links 里有许多的 property,你的搜寻条件应该是Model_links 里的 property,否则 CLR 怎知道您的比对条件为何,Dictionary 集合里的 Key 通常都是摆放实值型别(int,long) 或 string,你这样做只是找自己麻烦,两个对象的比较 http://msdn.microsoft.com/zh-cn/library/dd183752.aspx

    List 集合同样也是要比对 Model_links 里的 property

    private void button1_Click(object sender, EventArgs e)
    {
        Model_Links expect = new Model_Links() { id = 4, title = "title4", body = "boby4" };
    
        List<Model_Links> targetList = new List<Model_Links>()
            {
                new Model_Links() { id = 1, title = "title1", body = "boby1" },
                new Model_Links() { id = 2, title = "title2", body = "boby2" },
                new Model_Links() { id = 3, title = "title3", body = "boby3" },
                new Model_Links() { id = 4, title = "title4", body = "boby4" }
            };
    
        var query = (from model in targetList
                     where model.id == expect.id
                     select model).FirstOrDefault();
    
        if (query != null)
        {
            MessageBox.Show(string.Format("Found ID :{0}", query.id));
        }
    }


    你也可以利用序列化,将对象转成bytes,然后再比较两者的byte顺序,参考以下代码及link

    http://www.dotblogs.com.tw/yc421206/archive/2012/05/25/72390.aspx

    private void button2_Click(object sender, EventArgs e)
    {
        byte[] test1 = null;
        byte[] test2 = null;
        Model_Links expect = new Model_Links() { id = 3, title = "title3", body = "boby3" };
    
        List<Model_Links> targetList = new List<Model_Links>()
            {
                new Model_Links() { id = 1, title = "title1", body = "boby1" },
                new Model_Links() { id = 2, title = "title2", body = "boby2" },
                new Model_Links() { id = 3, title = "title3", body = "boby3" },
                new Model_Links() { id = 4, title = "title4", body = "boby4" }
            };
    
        using (MemoryStream expectMemory = new MemoryStream())
        {
            IFormatter formatter = new BinaryFormatter();
            formatter.Serialize(expectMemory, expect);
            expectMemory.Seek(0, SeekOrigin.Begin);
            test1 = expectMemory.ToArray();
        }
    
        foreach (var model in targetList)
        {
            using (MemoryStream expectMemory = new MemoryStream())
            {
                IFormatter formatter = new BinaryFormatter();
                formatter.Serialize(expectMemory, model);
                expectMemory.Seek(0, SeekOrigin.Begin);
                test2 = expectMemory.ToArray();
            }
            bool areEqual = test1.SequenceEqual(test2);
            if (areEqual)
            {
                break;
            }
        }
    }

    上面的代码看起来很繁杂,但也是个人目前认为最有效的方法之一。

    或是参考以下

    http://msdn.microsoft.com/zh-tw/library/dd183755.aspx

    http://msdn.microsoft.com/zh-tw/library/system.collections.iequalitycomparer(v=vs.80).aspx[补充]

    想说明的是:类的比较不能直接比较,我建议你可以仿造我的List写法使用LINQ查询手段去找寻是否存在一个你已经定义的实体(尝试使用Dictionary的Keys属性+LINQ等语句进行比较)。


    秘訣無它,唯勤而已 http://www.dotblogs.com.tw/yc421206/



    2012年12月17日 7:25
  • @我知道要重写IEquityComparer,但是不知道具体如何操作。新手呀。求代码。

    namespace CSharp
    {
        using System.Threading.Tasks;
        using System.Xml.Linq;
        using System.Linq;
        using System.Data;
        using System.Net;
        using System.Text;
        using System.IO;
        using System;
        using System.Collections.Generic;
        using System.Linq.Expressions;
     
        public partial class Model_Links
        {
            public int id { getset; }
            public string title { getset; }
            public string body { getset; }
        }
     
        public class MyComparer:IEqualityComparer<KeyValuePair<Model_Linksint>>
        {
            public bool Equals(KeyValuePair<Model_Linksint> x, KeyValuePair<Model_Linksint> y)
            {
                return x.Key.id == y.Key.id && x.Key.title == y.Key.title && x.Key.body == y.Key.body;
            }
     
            public int GetHashCode(KeyValuePair<Model_Linksint> obj)
            {
                return obj.Key.id.GetHashCode() ^ obj.Key.title.GetHashCode() ^ obj.Key.body.GetHashCode();
            }
        }
     
        class Program
        {
            static void Main(string[] args)
            {
                Dictionary<Model_Linksint> usedLinks = new Dictionary<Model_Linksint>();
                usedLinks.Add(new Model_Links() { id = 1, title = "title1", body = "boby1" }, 1);
                usedLinks.Add(new Model_Links() { id = 2, title = "title2", body = "boby2" }, 2);
                usedLinks.Add(new Model_Links() { id = 3, title = "title3", body = "boby3" }, 3);
                usedLinks.Add(new Model_Links() { id = 4, title = "title4", body = "boby4" }, 4);
     
                //对比的类
                KeyValuePair<Model_Linksint> m = new KeyValuePair<Model_Linksint>(new Model_Links { id = 1, title = "title1", body = "boby1" }, 1);
     
                //测试是否存在
                Console.WriteLine(usedLinks.Contains(m,new MyComparer()));
            }
        }
    }

    我的博客园
    慈善点击,点击此处
    和谐拯救危机,全集下载,净化人心

    2012年12月17日 8:09
    版主
  • 楼主:

    你无法直接使用ContainsKey去对一个类实体比较。因为类和类永远不会“相等”(是比较hashCode)的。查看源代码如下:

     /// <summary>
        /// Determines whether the <see cref="T:System.Collections.Generic.Dictionary`2"/> contains the specified key.
        /// </summary>
        /// 
        /// <returns>
        /// true if the <see cref="T:System.Collections.Generic.Dictionary`2"/> contains an element with the specified key; otherwise, false.
        /// </returns>
        /// <param name="key">The key to locate in the <see cref="T:System.Collections.Generic.Dictionary`2"/>.</param><exception cref="T:System.ArgumentNullException"><paramref name="key"/> is null.</exception>
        [__DynamicallyInvokable]
        public bool ContainsKey(TKey key)
        {
          return this.FindEntry(key) >= 0;
        }

    继续跟踪:

     private int FindEntry(TKey key)
        {
          if ((object) key == null)
            ThrowHelper.ThrowArgumentNullException(ExceptionArgument.key);
          if (this.buckets != null)
          {
            int num = this.comparer.GetHashCode(key) & int.MaxValue;
            for (int index = this.buckets[num % this.buckets.Length]; index >= 0; index = this.entries[index].next)
            {
              if (this.entries[index].hashCode == num && this.comparer.Equals(this.entries[index].key, key))
                return index;
            }
          }
          return -1;
        }


    @花落花开:作者已经要求重写这个接口,所以你的回答是冗余的(因为作者只是不知道如何重写)故我删除了,谢谢!

    @小章:我明白你的意思,不过貌似作者初次学习代码,所以当你给了一个List的比较恐怕作者无法理解。建议你修改成Dictionary方式的Where扩展比较好。


    我的博客园
    慈善点击,点击此处
    和谐拯救危机,全集下载,净化人心

    2012年12月17日 8:29
    版主
  • 您的答案让我茅塞顿开,非常感谢,学习到了2个很厉害的方法。
    2012年12月19日 8:04
  • 刚好是我需要的,非常感谢。
    2012年12月19日 8:04
  • 大概明白您 代码的意思,不过感觉有点看不大懂。还是非常感谢您。
    2012年12月19日 8:06
  • dear

    基本上是不建议使用class 来当 key,不过还是希望透过讨论能带出更多的基础观念

    你可参考以下了解 Equals 的用法以及 GetHashCode 的功用
    http://www.dotblogs.com.tw/yc421206/archive/2012/12/17/85732.aspx
    http://www.dotblogs.com.tw/larrynung/archive/2009/12/16/12489.aspx

    只要是类方法带有前缀 Contains 关键字,都会用到 GetHashCode,CLR 是用 HashCode(杂凑) 来产判断集合是否存在某个物件。比如 Dictionary.ContainsKey / AddList.Contains,那 Dictionary.ContainsValue 有没有用到 HashCode?就留给您当研旧题目了。


    秘訣無它,唯勤而已 http://www.dotblogs.com.tw/yc421206/


    2012年12月19日 16:44