如何使用内部键作为自定义类访问和分配嵌套字典中的值?

时间:2018-08-24 09:47:28

标签: c# dictionary

我正在尝试根据内部字典的键访问和分配值,该字典恰好是一个自定义对象PlanTier,它具有2个属性PlanName和Tiers。

问题从这里开始: curEnrollByEnrolledOrWaived [group.Key] [waivedPlanTier] =                             enrByGroupAndPlan [group.Key] [waivedPlanTier];

请注意,虽然预先填充了字典“ curEnrollByEnrolledOrWaived”和“ enrByGroupAndPlan”(作为输入参数),但PlanTier对象“ waivedPlanTier”(我正试图使用​​(作为键)在这些字典中查找相应的值)是在此函数中初始化。

我已阅读到该问题是由对象引用引起的,因此这两个词典中确实包含与“ waivedPlanTier”具有相同属性的键。另一件事,

问题: 是否可以通过将内部字典的键与“ waivedPlanTier”等同来获取内部字典的相应值?如果是这样,我该怎么办?

如果不可能,是否还有其他方法可以使用“ waivedPlanTier”基于键访问和分配值?

代码如下:

        private static Dictionary<string, Dictionary<PlanTier, double>> PopulateEnrollOrWaived(Dictionary<string, Dictionary<PlanTier, double>> fullListByGroupAndEnrStatus, Dictionary<string, Dictionary<PlanTier, double>> enrByGroupAndPlan, List<string> distinctTierList) (Dictionary<string, Dictionary<PlanTier, double>> fullListByGroupAndEnrStatus
        , Dictionary<string, Dictionary<PlanTier, double>> enrByGroupAndPlan
        , List<string> distinctTierList)
    {

        Dictionary<string, Dictionary<PlanTier, double>> curEnrollByEnrolledOrWaived =
            fullListByGroupAndEnrStatus.ToDictionary(x => x.Key, x => x.Value.ToDictionary(y => y.Key, y => y.Value));

        PlanTierComparer comparer = new PlanTierComparer();

        foreach (var group in curEnrollByEnrolledOrWaived)
        {
            foreach (var tier in distinctTierList)
            {
                if (enrByGroupAndPlan.ContainsKey(group.Key))
                {
                    PlanTier waivedPlanTier = new PlanTier(Enums.Literals.WaivedPlanName, tier);
                    PlanTier enrolledPlanTier = new PlanTier("Enrolled", tier);

                    var test = curEnrollByEnrolledOrWaived[group.Key];

                    var test1 = test.Keys.Where(x => x.Equals(waivedPlanTier));                                      

                    curEnrollByEnrolledOrWaived[group.Key][waivedPlanTier] =
                        enrByGroupAndPlan[group.Key][waivedPlanTier];

                    curEnrollByEnrolledOrWaived[group.Key][enrolledPlanTier] =
                        enrByGroupAndPlan[group.Key].Values.Sum()
                        - curEnrollByEnrolledOrWaived[group.Key][waivedPlanTier];
                }
            }

        }
        return curEnrollByEnrolledOrWaived;
    }

1 个答案:

答案 0 :(得分:0)

默认情况下,按引用检查引用类型是否相等:new PlanTier("plan2", "tier1").Equals(new PlanTier("plan2", "tier1"))始终为false。因此,如果您将引用类型用作字典键,则查找也将通过引用进行。

当您需要按值查找时,基本上有两个选择:

  • 像这样执行IEqualityComparer<T>

    class PlanTierComparer : IEqualityComparer<PlanTier>
    {
        public static readonly PlanTierComparer Instance = new PlanTierComparer();
    
        PlanTierComparer() { }
    
        public bool Equals(PlanTier x, PlanTier y)
        {
            if (x == null && y == null)
                return true;
    
            if (x == null || y == null)
                return false;
    
            return x.PlanName == y.PlanName && x.Tiers == y.Tiers;
        }
    
        public int GetHashCode(PlanTier obj)
        {
            return obj != null ? obj.PlanName.GetHashCode() ^ obj.Tiers.GetHashCode() : 0;
        }
    }
    

    (我假设 PlanName Tiers 属性不能为空。)

    并使用此比较器创建字典:

    var pt1 = new PlanTier("plan1", "tier1");
    var pt2 = new PlanTier("plan1", "tier2");
    var pt3 = new PlanTier("plan2", "tier1");
    
    // Dictionary ctor example
    var dic1 = new Dictionary<PlanTier, double>(PlanTierComparer.Instance) { { pt1, 1.0 }, { pt2, 2.0 }, { pt3, 3.0 } };
    
    // ToDictionary() example
    var dic2 = new[]
    {
        new KeyValuePair<PlanTier, double>(pt1, 1.0),
        new KeyValuePair<PlanTier, double>(pt2, 2.0),
        new KeyValuePair<PlanTier, double>(pt3, 3.0),
    }.ToDictionary(p => p.Key, p => p.Value, PlanTierComparer.Instance);
    
  • 或者仅使您的键类型实现IEquatable<T>

    class PlanTier : IEquatable<PlanTier>
    {
        public PlanTier(string planName, string tiers)
        {
            PlanName = planName ?? throw new ArgumentNullException(nameof(planName));
            Tiers = tiers ?? throw new ArgumentNullException(nameof(tiers));
        }
    
        public string PlanName { get; }
        public string Tiers { get; }
    
        public override bool Equals(object obj)
        {
            return obj is PlanTier other ? Equals(other) : false;
        }
    
        public override int GetHashCode()
        {
            return PlanName.GetHashCode() ^ Tiers.GetHashCode();
        }
    
        public bool Equals(PlanTier other)
        {
            if (other == null)
                return false;
    
            return PlanName == other.PlanName && Tiers == other.Tiers;
        }
    }
    

    您还需要重写 GetHashCode Equals(object)方法,以使其正常工作。 (实际上,您可以仅重写这两种方法,而不必实现 IEquatable 。但是,建议这样做以提高性能,尤其是在这种情况下,值类型(结构)以避免装箱。)

使用复杂类型作为字典键时要记住的另一件事:键对象应该是不可变!换句话说,键对象的状态在创建后不应更改,因为这可能导致哈希码发生更改,从而破坏字典的功能。