C# 使用可变键从集合中检索项的有效方法

C# 使用可变键从集合中检索项的有效方法,c#,excel,dictionary,vsto,mutable,C#,Excel,Dictionary,Vsto,Mutable,我有一个具有属性FooPosition的项目Foos集合,我需要通过它们的位置快速访问Foos 例如:检索位于X=0和Y=1的Foo 我的第一个想法是为此目的使用字典,并使用FooPosition作为字典键。我知道我的收藏中的每个位置都是独一无二的,如果不是这样,我不介意抛出一个异常 只要Foos不到处移动,这种方法就很有效 但是,正如我所理解的那样,感谢和帖子,如果职位被更新,这将不再有效我不应该在字典中使用可变键:字典将FooPosition哈希代码保存在内存中,但在修改底层FooPosit

我有一个具有属性FooPosition的项目Foos集合,我需要通过它们的位置快速访问Foos

例如:检索位于X=0和Y=1的Foo

我的第一个想法是为此目的使用字典,并使用FooPosition作为字典键。我知道我的收藏中的每个位置都是独一无二的,如果不是这样,我不介意抛出一个异常

只要Foos不到处移动,这种方法就很有效

但是,正如我所理解的那样,感谢和帖子,如果职位被更新,这将不再有效我不应该在字典中使用可变键:字典将FooPosition哈希代码保存在内存中,但在修改底层FooPosition时不会更新它。因此,调用dic[Position(0,1)]可以得到构建字典时位于该位置的Foo

因此,我现在想知道应该使用什么来高效地按位置检索foo

我所说的高效,是指每次我按位置查询一个Foo时,不会遍历整个集合。是否有适合容纳可变钥匙的结构

谢谢你的帮助

编辑

正如在评论中正确提到的,我的问题中缺少了一个部分:我无法控制Foo的移动。该软件实际上通过COM协议连接到另一个软件(通过VSTO连接Excel),该协议在不通知更改的情况下更改位置(Excel范围)

因此,我不能采取任何行动,以防移动发生,因为我不知道发生了变化

public class FooManager
{
    public void DoSomething(IList<Foo> foos) {
        Dictionary<FooPosition, Foo> fooPositionDictionary = foos.ToDictionary(x => x.Position, x => x); //I know that position is unique

        //Move Foos all around the place by changing their positions. 

        FooPosition queryPosition = new FooPosition(0, 1);

        fooPositionDictionary.TryGetValue(queryPosition, out var foo1); //DOES NOT WORK
        var foo2 = foos.FirstOrDefault(x => x.Position == queryPosition); //NOT EFFICIENT

        //Any better idea?
    }
}

public class Foo
{
    public string Name { get; set; }
    public FooPosition Position { get; set; }
}

public class FooPosition : IEquatable<FooPosition>
{
    public int X { get; set; }
    public int Y { get; set; }

    public FooPosition(int x, int y)
    {
        X = x;
        Y = y;
    }

    public void MoveBy(int i)
    {
        X = X + i;
        Y = Y + i;
    }

    public bool Equals(FooPosition other)
    {
        if (ReferenceEquals(null, other)) return false;
        if (ReferenceEquals(this, other)) return true;
        return X == other.X && Y == other.Y;
    }

    public override bool Equals(object obj)
    {
        if (ReferenceEquals(null, obj)) return false;
        if (ReferenceEquals(this, obj)) return true;
        if (obj.GetType() != this.GetType()) return false;
        return Equals((FooPosition) obj);
    }

    public override int GetHashCode()
    {
        unchecked
        {
            return (X * 397) ^ Y;
        }
    }

    public static bool operator ==(FooPosition left, FooPosition right)
    {
        return Equals(left, right);
    }

    public static bool operator !=(FooPosition left, FooPosition right)
    {
        return !Equals(left, right);
    }
}
公共类管理器
{
公共无效剂量测定(IList-foos){
Dictionary-foositiondictionary=foos.ToDictionary(x=>x.Position,x=>x);//我知道这个位置是唯一的
//通过改变食物的位置来移动食物。
FooPosition queryPosition=新的FooPosition(0,1);
fooPositionDictionary.TryGetValue(queryPosition,out var foo1);//不起作用
var foo2=foos.FirstOrDefault(x=>x.Position==queryPosition);//效率不高
//还有更好的主意吗?
}
}
公开课Foo
{
公共字符串名称{get;set;}
公共位置{get;set;}
}
公共类FooPosition:IEquatable
{
公共整数X{get;set;}
公共整数Y{get;set;}
公共位置(整数x,整数y)
{
X=X;
Y=Y;
}
公共空间移动人(int i)
{
X=X+i;
Y=Y+i;
}
公共布尔等于(其他)
{
if(ReferenceEquals(null,other))返回false;
if(ReferenceEquals(this,other))返回true;
返回X==other.X&&Y==other.Y;
}
公共覆盖布尔等于(对象对象对象)
{
if(ReferenceEquals(null,obj))返回false;
if(ReferenceEquals(this,obj))返回true;
if(obj.GetType()!=this.GetType())返回false;
返回等于((位置)obj);
}
公共覆盖int GetHashCode()
{
未经检查
{
报税表(X*397)^Y;
}
}
公共静态布尔运算符==(左FooPosition,右FooPosition)
{
返回等于(左、右);
}
公共静态布尔运算符!=(FooPosition左,FooPosition右)
{
返回!等于(左,右);
}
}

从某种意义上讲,字典——就像任何其他基于散列的数据存储一样——使用某种缓存。在这种情况下,哈希被缓存。但是,对于每个缓存,您需要一些在数据存储的生命周期内不会更改的常量数据。如果没有这样的常量数据,就没有办法有效地缓存这些数据


因此,您最终将所有项存储在某个线性集合中,例如,一个
列表,并反复迭代该列表。

从某种意义上讲,字典与任何其他基于散列的数据存储一样,使用某种缓存。在这种情况下,哈希被缓存。但是,对于每个缓存,您需要一些在数据存储的生命周期内不会更改的常量数据。如果没有这样的常量数据,就没有办法有效地缓存这些数据


因此,您最终将所有项目存储在某个线性集合中—例如,一个
列表
—并反复迭代该列表。

“(这是一个COM协议接口)”这是您在问题中真正应该提到的一个关键部分。如果您无法控制位置何时更新,如果数据存储甚至不知道发生了任何更改,您希望它如何更新其内容?无法保持存储同步。因此,唯一的方法是在地图中有一个常量标识符,并迭代地图中的项目——即使这看起来不太有效?在下一次换岗之前,您要查询多少项?你分析过它吗?“效率不高”的方式是否存在性能问题?`@Fildor你是对的,我更新了它。我不知道这是否适用于您的情况,但解决此类Excel问题的通常方法是使用命名范围,并在代码中引用命名范围,而不是工作表和单元格坐标-移动命名范围时,Excel会更新命名范围的refresto公式属性。或者,您可以使用excel事件来告知代码何时发生移动。”(这是COM协议接口)“这是您在问题中真正应该提到的关键部分。如果您无法控制职位更新的时间,如果数据存储甚至不知道发生了任何更改,您希望它如何更新其内容?无法保持存储同步。因此,唯一的方法是在映射中有一个常量标识符,并迭代该映射中的项-