首页 > 解决方案 > 使用 C# 中的字典在同一类中使用某些自定义类属性作为另一个属性(形成为值)的唯一键

问题描述

我有一个类类型,如下所示的 ValueSetting,我正在尝试迭代 List<ValueSetting>,以便对于列表中 param1、param2、param3 的所有常见条目,我必须对每个 param4 执行特定操作。让我解释 。在下表中,带有红色箭头标记的条目 (param1, param2, param3) 应该类似于 param4(500, 600) 的 list< values > 的键。所以巩固他们

10, 富, abc => (500, 600)

20, 嘘, abc =>( 500, 600)

30, 富, abc => (500)

那么,这是我可以将 param1、param2、param3 作为 List< param4 values > 的键放入字典中的吗?这是怎么做的?还是有更好的方法来存储它们而不是字典?

在此处输入图像描述

public class ValueSetting 
{  
  public int param1;
  public string param2;
  public string param3;
  public int param4;
}

标签: c#linqdictionarydata-structures

解决方案


LINQ ToLookup method可以使用您要求的方式来构造数据。来自 MSDN:

ToLookup方法返回一个Lookup,一个将键映射到值集合的一对多字典。ALookup与 a 不同Dictionary,后者执行键到单个值的一对一映射。

要创建LookupusingToLookup方法,我们应该定义:

  • key具有重写GetHashCodeEquals方法的类;此类key将定义如何对输入数据进行分组;
  • keySelector这将用于从输入数据创建密钥;
  • elementSelector这将用于为适当的键选择值。

第一种方法:使用匿名类型作为键。

我们可以使用匿名类型作为键来创建一个Lookup. Anonymous types自动实现GetHashCodeEquals。来自 MSDN:

因为匿名类型的 Equals 和 GetHashCode 方法是根据属性的 Equals 和 GetHashCode 方法定义的,所以相同匿名类型的两个实例只有在它们的所有属性都相等时才相等。

以下是我们如何创建Lookupusinganonymous type作为键:

var list = new List<ValueSetting>
{
    new ValueSetting {param1 = 10, param2 = "foo", param3 = "abc", param4 = 500},
    new ValueSetting {param1 = 20, param2 = "boo", param3 = "abc", param4 = 500},
    new ValueSetting {param1 = 10, param2 = "foo", param3 = "abc", param4 = 600},
    new ValueSetting {param1 = 20, param2 = "boo", param3 = "abc", param4 = 600},
    new ValueSetting {param1 = 30, param2 = "foo", param3 = "abc", param4 = 500},
};

var lookup = list.ToLookup(
    vs => new {vs.param1, vs.param2, vs.param3}, // Group key.
    vs => vs.param4);                            // Group values.

foreach (var item in lookup)
{
    Console.WriteLine("{0} => ({1})", item.Key, string.Join(", ", item.Select(i => i)));
}

如果您打算Lookup在单个方法中使用这种方法很方便,因为匿名类型的对象不能从该方法返回并作为参数传递给另一个方法。


第二种方法:引入一个关键类。

另一种方法是定义一个将用作分组键的类。在这个类中,我们应该覆盖GetHashCodeEquals方法。如果您使用 Visual Studio 2015 或更高版本,则可以自动生成这些成员。下面是我们如何定义这样的类:

public class Key
{
    public int param1;
    public string param2;
    public string param3;

    public override bool Equals(object obj)
    {
        if (ReferenceEquals(null, obj)) return false;
        if (ReferenceEquals(this, obj)) return true;
        if (obj.GetType() != GetType()) return false;
        Key other = (Key) obj;
        Key other = (Key) obj;
        return param1 == other.param1 && 
               string.Equals(param2, other.param2) && 
               string.Equals(param3, other.param3);
    }

    public override int GetHashCode()
    {
        unchecked
        {
            var hashCode = param1;
            hashCode = (hashCode * 397) ^ (param2 != null ? param2.GetHashCode() : 0);
            hashCode = (hashCode * 397) ^ (param3 != null ? param3.GetHashCode() : 0);
            return hashCode;
        }
    }
}

然后使用这个关键类,我们可以创建一个Lookup

var list = new List<ValueSetting>
{
    new ValueSetting {param1 = 10, param2 = "foo", param3 = "abc", param4 = 500},
    new ValueSetting {param1 = 20, param2 = "boo", param3 = "abc", param4 = 500},
    new ValueSetting {param1 = 10, param2 = "foo", param3 = "abc", param4 = 600},
    new ValueSetting {param1 = 20, param2 = "boo", param3 = "abc", param4 = 600},
    new ValueSetting {param1 = 30, param2 = "foo", param3 = "abc", param4 = 500},
};

ILookup<Key, int> lookup = list.ToLookup(
    vs => new Key {param1 = vs.param1, param2 = vs.param2, param3 = vs.param3}, // Group key.
    vs => vs.param4); // Group values.

foreach (IGrouping<Key, int> item in lookup)
{
    Console.WriteLine("{0}, {1}, {2} => ({3})",
        item.Key.param1, item.Key.param2, item.Key.param3,
        string.Join(", ", item.Select(i => i)));
}

如果您想Lookup从方法返回,或将其作为参数传递给另一个方法,或存储为类的属性,则可以使用此方法(在其他情况下,创建时Lookup必须在创建它的方法之外使用)。


推荐阅读