【问题标题】:Dictionary.ContainsKey() in C# is not finding a key in the Dictionary [duplicate]C# 中的 Dictionary.ContainsKey() 在字典中找不到键[重复]
【发布时间】:2015-03-01 07:07:31
【问题描述】:

我正在尝试为特权/权限编写一个类。

我正在尝试在 C# 中使用二进制基本权限系统。

我在我认为它应该工作的地方得到了它。但是,它没有按预期工作。

之后,我逐步检查了我的代码,我可以在名为 _mapPermissions 的方法中看到一个问题,if (dic.ContainsKey(vv)) 行每次都返回 false。

键是具有 2 个值的类的实例(即secionNameKeyIndex

这是我的整个班级

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using MySql.Data.MySqlClient;

namespace POS
{
    class Roles
    {

        private Dictionary<PermissionsKey, int> systemPermissions = new Dictionary<PermissionsKey, int>();
        private Dictionary<PermissionsKey, int> userPermissions = new Dictionary<PermissionsKey, int>();

        public Roles()
        {
            this._getSystemPrivleges();
            this._getUserPrivleges();

            this._display(systemPermissions);
            this._display(userPermissions);

        }

        public bool hasAccess(string sectionName, string[] keys)
        {

            if (    this.systemPermissions.Count == 0
                 || this.userPermissions.Count == 0
                 || keys.Count() == 0
                 || String.IsNullOrEmpty(sectionName) 
             )
            {
                return false;
            }

            int systemReq = this._mapPermissions(sectionName, keys, systemPermissions); ;
            int userPerm = this._mapPermissions(sectionName, keys, userPermissions);



            int check =  systemReq & userPerm;

            Common.Alert("System Value" + systemReq + Environment.NewLine +"User Value " + userPerm + Environment.NewLine + "AND Results " + check);


            if (check == 0)
            {
                return false;
            }

            return true;

        }

        private int _mapPermissions(string secName, string[] keys, Dictionary<PermissionsKey, int> dic)
        {

            int newKey = 0;
            var vv = new PermissionsKey();

            foreach (string k in keys)
            {
                vv.sectionName = secName;
                vv.KeyIndex = k;

                if (dic.ContainsKey(vv))
                {
                    newKey |= dic[vv] ;
                }
            }

            return newKey;
        }



        private void _getSystemPrivleges()
        {
            var db = new dbConnetion();

            string sql =   " SELECT SQL_CACHE "
                         + " KeyIndex, Value, sectionName "
                         + " FROM role_keys "
                         + " WHERE status = 'active' ";

            foreach (var i in db.getData(sql, null, r => new SystemPermissions()
                                                           {
                                                                 pIndex = r["KeyIndex"].ToString()
                                                               , pSec = r["sectionName"].ToString()
                                                               , pValue = r["Value"].ToString()
                                                           }
                                           )
                    )
            {

                var vv = new PermissionsKey();
                vv.sectionName = i.pSec;
                vv.KeyIndex = i.pIndex;
                systemPermissions.Add(vv, Convert.ToInt32(i.pValue));

            }
        }

        private void _getUserPrivleges()
        {
            var db = new dbConnetion();

            string sql = " SELECT SQL_CACHE k.sectionName, k.KeyIndex, k.value "
                        +" FROM users AS su "
                        +" INNER JOIN role_relation AS r ON r.roleID = su.roleID "
                        +" INNER JOIN role_keys AS k ON k.KeyID = r.KeyID "
                        +" WHERE su.status = 'active' AND su.userID = @userid ";

            var parms = new List<MySqlParameter>();
            parms.Add(new MySqlParameter("@userid", UserInfo.UserID));

            foreach (var i in db.getData(sql, parms , r => new SystemPermissions()
                                                            {
                                                                  pIndex = r["KeyIndex"].ToString()
                                                                , pSec = r["sectionName"].ToString()
                                                                , pValue = r["Value"].ToString()
                                                            }
                                           )
                    )
            {
                var vv = new PermissionsKey();
                vv.sectionName = i.pSec;
                vv.KeyIndex = i.pIndex;

                userPermissions.Add(vv, Convert.ToInt32(i.pValue));
            }
        }



        private void _display(Dictionary<PermissionsKey, int> dic)
        {
            string str = "";
            foreach (KeyValuePair<PermissionsKey, int> d in dic)
            {
                var vv = new PermissionsKey();
                var c = d.Key;

                str += c.sectionName + "_" + c.KeyIndex + "  =>  " + d.Value.ToString() + Environment.NewLine;

            }

            Common.Alert(str);
        }



        private int _BinToInt(string str)
        {
            Regex binary = new Regex("^[01]{1,32}$", RegexOptions.Compiled);
            int val = -1;

            if (binary.IsMatch(str))
            {
                val = Convert.ToInt32(str, 2);
            }

            return val;
        }

        private string _IntToBin(int number)
        {
            return Convert.ToString(number, 2);
        }


    }

    class SystemPermissions{
        public string pIndex;
        public string pSec;
        public string pValue;
    }

    class PermissionsKey
    {
        public string sectionName;
        public string KeyIndex;
    }
}

我就是这样使用这个类的

    var role = new Roles();
    string[] aa = { "view", "use" };

    if (role.hasAccess("system", aa))
    {
        Common.Alert("Welcome");
    }
    else
    {
        Common.Alert("NOP!");
    }

注意Common.Alert() 方法只是显示一条消息(即MessageBox

以上代码运行时显示如下

1) systemPermissions 字典的内容按以下顺序排列 PermissionsKey.sectionName_PermissionsKey.KeyIndex => 值 (ie. 1 , 2, 4, 8, 16, 32, 64 .....) system_do = > 1 系统使用 => 2 系统视图 => 4 测试使用 => 1 test_view => 2

2)usePermissions字典的内容

system_do = > 1
test_use => 1

问题是systemRequserPerm 这两个变量每次都返回0。我不知道为什么

 int systemReq = this._mapPermissions(sectionName, keys, systemPermissions); ;
 int userPerm = this._mapPermissions(sectionName, keys, userPermissions);

【问题讨论】:

  • 你可能需要在你的 PermissionKey 类型上定义 gethashcode 和 equals,否则字典不知道什么时候两个东西是等价的。
  • 你能告诉我你所说的 gethashcode 是什么意思吗?
  • 散列基本上是通过将项目分割成桶来工作的。例如,如果您有 100 个项目和 10 个存储桶,那么您只需确定项目属于哪个存储桶(通过使用 GetHashCode),然后在该存储桶中进行搜索。这将搜索从 100 项减少到仅搜索 10 项(这就是字典如此快的原因)。也就是说,要让它知道某物进入哪个存储桶,您需要定义一个散列函数。
  • C#/Java 中的散列函数基本上通过重新调整数字 (Int32) 来工作。数字是多少并不重要,只要您始终为相同的“类型”获得相同的数字(无论这在您的实现中意味着什么)。所以两个相等的东西也应该有相同的哈希码(这样它们就可以进入同一个桶——字典只会测试同一个桶中两个项目的相等性!)。例如,一个简单的解决方案就是返回(KeyIndex+sectionName).getHashCode()。这是原始的,但我认为它会在你的场景中工作。
  • 提示:考虑在 Google/Bing 上搜索您的问题的标题。 (所以搜索引擎有时不会带来最佳匹配)以验证您是否真的是第一个遇到问题的人......我怀疑有些人已经尝试使用自定义对象作为字典中的键,并且确实根据这个搜索@ 987654321@有些人已经遇到过类似的问题。

标签: c# authentication dictionary permissions user-permissions


【解决方案1】:

为了将某事物用作字典键并期望它遵循您对被视为等于的期望,您实际上需要定义被视为相同的事物。

在您的PermissionsKey 类中,您没有覆盖Equals,这意味着默认的Equals 方法仍在使用(这是对象引用,并且对于每个特定对象都是唯一的)。

要解决此问题,您需要实际告诉字典如何评估相等性。并且无论何时你覆盖Equals,你也应该also override GetHashCode

class PermissionsKey
{
    private string sectionName;
    private string keyIndex;

    public string SectionName { get { return sectionName; } }
    public string KeyIndex { get { return keyIndex; } }

    public PermissionsKey(string sectionName, string keyIndex)
    {
        this.sectionName = sectionName;
        this.keyIndex = keyIndex;
    }

    public override bool Equals(object obj)
    {
        var key = obj as PermissionsKey;

        if (key == null)
            return false;

        return sectionName.Equals(key.sectionName) &&
               keyIndex.Equals(key.keyIndex);
    }


    //Credit to Jon Skeet from https://stackoverflow.com/questions/263400/what-is-the-best-algorithm-for-an-overridden-system-object-gethashcode
    //  for inspiration for this method
    public override int GetHashCode()
    {
        unchecked // Overflow is fine, just wrap
        {
            int hash = (int) 2166136261;
            // Suitable nullity checks etc, of course :)
            hash = hash * 16777619 ^ sectionName.GetHashCode();
            hash = hash * 16777619 ^ keyIndex.GetHashCode();
            return hash;
        }
    }
}

您正在重写 Equals 以实际定义相等比较。每当您尝试比较 2 个对象时(特别是在 Dictionary 对象中,但在其他任何时候您通过调用 .Equals 进行比较时),您都应该定义自己的比较。如果你不这样做,那么你只是在使用默认的相等比较,这是一个纯粹的反对比较,除非从同一个确切的对象创建,否则 2 个对象永远不会相等。

同样,当您覆盖 Equals 时,强烈建议您也覆盖 GetHashCode。 HashCode 定义了对象属于哪个“桶”。遵循 Equals 实现的良好哈希码将通过仅将具有相同哈希码的对象放入相同的桶中来帮助加快比较速度。标准建议是 2 个等于的对象将始终具有相同的哈希码,但也有可能 2 个不等于的对象具有相同的哈希码。实际上,这是框架检查不平等的一种快速方法。如果你得到 2 个不同的哈希码,框架就知道对象不相等,但如果哈希码相同,它会检查是否相等。

如果您不能或不想覆盖 Equals 和 GetHashCode,那么您还可以选择在字典的构造函数中定义 IEqualityComparer&lt;T&gt;

您只需传递一个实现IEqualityComparer&lt;T&gt; 的对象,并在该对象中定义所需的相等比较。

此外,您计划用作字典键的任何内容绝不是可变的,特别是用于计算相等性和计算哈希码的字段,否则您的键可能会作为哈希丢失对象的代码与最初放入字典的代码不匹配。我修改了您的原始示例,将 PermissionsKey 中的字段更改为不可变属性,并添加了一个构造函数,允许您在最初创建时设置一次。

【讨论】:

  • 是的,就在这里。
  • 我不确定如何实现 Equals 或 GetHashCode。我从不使用那些,所以我不确定他们应该这样做。 Equals 背后的逻辑需要是什么样的。基本上sectionNameKeyIndex 的组合是字典中的一个键
  • @Mike for getashcode,查看我在答案中链接的帖子了解一些细节,但我会给你一个想法。
  • @Mike 不想变得苛刻,但你需要学习谷歌......只需在谷歌搜索中输入“GetHashCode”即可将我指向stackoverflow.com/questions/371328/… 以及msdn.microsoft.com/en-us/library/… 这是有关该主题的官方文档。
  • @Mike Hash 的概念并不是 c# 独有的。你会在大多数面向对象的语言中看到类似的东西。最简单的解释是哈希码是一个非唯一的 id。当您想说xy 是否相等时,那么它们必须具有相同的哈希码。但仅仅因为 2 个对象具有相同的 Hash Code 并不意味着它们是相等的。
猜你喜欢
  • 1970-01-01
  • 2013-05-09
  • 2021-04-04
  • 2018-01-12
  • 1970-01-01
  • 2020-03-08
  • 2018-09-27
  • 1970-01-01
  • 2023-03-25
相关资源
最近更新 更多