C# 将对象用作键时.Net MemoryCache未命中

C# 将对象用作键时.Net MemoryCache未命中,c#,.net,caching,memorycache,C#,.net,Caching,Memorycache,对对象使用IMemoryCache时,TryGetValue始终未命中。我试图用一个元组作为键,元组工作得非常好 这里的代码总是让我错过缓存: _cache.TryGetValue(("typeOfCache", query), out var something); if(something == null) _cache.CreateEntry(("typeOfCache", query)); 我使用的对象内部有列表列表,而不是没有字典/集合(没有随机排序的东西) 这是一个.net错误还是

对象使用
IMemoryCache
时,
TryGetValue
始终未命中。我试图用一个
元组
作为键,
元组
工作得非常好

这里的代码总是让我错过缓存:

_cache.TryGetValue(("typeOfCache", query), out var something);
if(something == null) _cache.CreateEntry(("typeOfCache", query));
我使用的对象内部有列表列表,而不是没有字典/集合(没有随机排序的东西)

这是一个.net错误还是我做的不对?

在内部使用一个
ConcurrentDictionary
,它反过来使用
对象的默认比较器
类型,该类型根据
对象.Equals和
对象.GetHashCode>的实际类型重写执行相等比较。在您的情况下,您的键是
ValueTuple
,无论您的
Query
类是什么。如果被比较实例的组件与当前实例的组件具有相同的类型,并且组件与当前实例的组件相等,并且相等性由每个组件的默认相等比较器确定,则计算结果为true

因此,如何执行相等比较取决于
查询的实现。如果此类型不重写
Equals
GetHashCode
,也不实现
IEquatable
,则执行引用相等,这意味着您仅在传递查询的同一实例时才获得相等。如果要更改此行为,应扩展
查询
类以实现
IEquatable

我还发现
CreateEntry
不会立即将新条目添加到缓存中。NET核心文档少得令人失望,因此我还没有找到预期的行为;但是,您可以通过调用
Set
来确保添加条目

例如:

using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Extensions.Caching.Memory;

class Program
{
    static void Main(string[] args)
    {
        var query1 = new Query { Parts = { new List<string> { "abc", "def", "ghi" } } };
        var query2 = new Query { Parts = { new List<string> { "abc", "def", "ghi" } } };

        var memoryCache = new MemoryCache(new MemoryCacheOptions());
        memoryCache.Set(("typeOfCache", query1), new object());
        var found = memoryCache.TryGetValue(("typeOfCache", query2), out var something);
        Console.WriteLine(found);
    }

    public class Query : IEquatable<Query>
    {
        public List<List<string>> Parts { get; } = new List<List<string>>();

        public bool Equals(Query other)
        {
            if (ReferenceEquals(this, other)) return true;
            if (ReferenceEquals(other, null)) return false;
            return this.Parts.Length == other.Parts.Length 
                && this.Parts.Zip(other.Parts, (x, y) => x.SequenceEqual(y)).All(b => b);
        }

        public override bool Equals(object obj)
        {
            return Equals(obj as Query);
        }

        public override int GetHashCode()
        {
            return this.Parts.SelectMany(p => p).Take(10).Aggregate(17, (acc, p) => acc * 23 + p?.GetHashCode() ?? 0);
        }
    }
}    
使用系统;
使用System.Collections.Generic;
使用System.Linq;
使用Microsoft.Extensions.Caching.Memory;
班级计划
{
静态void Main(字符串[]参数)
{
var query1=新查询{Parts={newlist{“abc”、“def”、“ghi”};
var query2=新查询{Parts={newlist{“abc”、“def”、“ghi”};
var memoryCache=new memoryCache(new MemoryCacheOptions());
Set((“typeOfCache”,query1),new object());
var found=memoryCache.TryGetValue((“typeOfCache”,query2),out var something);
控制台写入线(已找到);
}
公共类查询:IEquatable
{
公共列表部分{get;}=new List();
公共布尔等于(查询其他)
{
if(ReferenceEquals(this,other))返回true;
if(ReferenceEquals(other,null))返回false;
返回this.Parts.Length==other.Parts.Length
&&this.Parts.Zip(其他.Parts,(x,y)=>x.SequenceEqual(y)).All(b=>b);
}
公共覆盖布尔等于(对象对象对象)
{
返回等于(obj作为查询);
}
公共覆盖int GetHashCode()
{
返回这个.Parts.SelectMany(p=>p).Take(10).Aggregate(17,(acc,p)=>acc*23+p?.GetHashCode()??0);
}
}
}    

Ahhh,现在说得通了。我将确保对象覆盖等于或只是序列化它们。非常感谢。欢迎光临。不要忘记覆盖
GetHashCode
;字典使用它进行查找。两个相等的对象必须返回相同的散列。@Douglas-有没有可能用这些LINQ来揭开GetHashCode中的神秘面纱,如果查询的两个实例的部分具有相等的值,它如何确保这两个实例将生成相同的哈希代码?@bubbleking:
GetHashCode
方法将展开的
部分
列表中前十个元素的哈希代码组合在一起。
Aggregate
操作符使用流行的模式,使用素数组合多个散列码;看,那么,为什么是10?这会不会导致为两个包含11+元素的集合生成相同的哈希代码,即使其中的第11个(或更高)元素不相同?