【问题标题】:.NET set collection using identity.NET 使用标识的集合集合
【发布时间】:2016-04-12 14:39:12
【问题描述】:

是否存在任何适用于身份而不是哈希码/平等的集合类集合(如HashSet<T>)?如果我只想拥有 1000 个(或更多)对象,我不在乎是否被视为相等,但需要检查我之前是否已经收到过该对象?

[编辑]
特别是,在这种情况下,我收到了一个GameObject(来自一个游戏引擎,我无法控制它,我无法更改它的代码)存储在一个集合中。现在一般来说,使用哈希码和相等可能很好,它只是让我觉得(至少在这种特殊情况下)我不需要它。我只需要确保一个对象不会被重新添加到集合中。我不知道GetHashCode 或平等是否在我正在处理的类中被覆盖。

[编辑 2]
因此,尝试更多地描述我所说的身份。如果您在 C# 中创建两个 Objects(使用 new Object()),则有两种方法可以检查它们是否“相等”。一种是检查是否相等(使用Object.Equals==),另一种是检查它们是否同一个对象(引用相等)。如果我没记错的话,在 VB 中,您可以使用obj1 Is obj2 来执行此操作。这是通过身份而不是平等来检查。换句话说,我正在寻找一个适用于引用相等的集合,而不是可覆盖的集合。

【问题讨论】:

  • 如果您创建一个具有哈希集的自定义类型并在插入之前检查此条件会怎样?
  • 或许Dictionary<TUniqueKey, TValue> 就够了?
  • 这不会有太大帮助,因为我的班级的消费者给了我TValues。即,我无法将其绑定到TUniqueKey(没有Dictionary<TValue, TUniqueKey>,这会破坏整个目的)。
  • @Alxandr 你说的身份是什么意思??
  • 查看更新了解我的意思的更多详细信息。

标签: .net collections


【解决方案1】:

你可以创建一个实现IList接口的类,在这个类里面你会有一个List,它会保存对象,你会通过list几乎所有的方法,除了:Add包含 InsertSET[Index],您将使用方法ReferenceEquals 来检查两个对象是否具有相同的引用。

如果你有这门课

public class Person
{
    protected bool Equals(Person other) => Id == other?.Id;

    public override int GetHashCode() => Id;

    public static bool operator ==(Person left, Person right) => Equals(left, right);

    public static bool operator !=(Person left, Person right) => !Equals(left, right);

    public int Id { get; set; }

    public override bool Equals(object obj) => Equals(obj as Person);
}

如果你运行这段代码

var person = new Person() { Id = 1 };
var person2 = new Person() { Id = 1 };
Console.WriteLine(person == person2);

你会发现两个人都是平等的。

现在让我们创建一个ListByReference<T>

public class ListByReference<T> : IList<T>
{
    private readonly List<T> _list;

    public ListByReference()
    {
        _list = new List<T>();
    }

    public IEnumerator<T> GetEnumerator() => _list.GetEnumerator();

    IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();

    public void Add(T item)
    {
        if (ObjectExists(item))
            return;
        _list.Add(item);
    }

    public void Clear() => _list.Clear();

    public bool Contains(T item) => ObjectExists(item);

    public void CopyTo(T[] array, int arrayIndex) => _list.CopyTo(array, arrayIndex);

    public bool Remove(T item) => _list.Remove(item);

    public int Count => _list.Count;
    public bool IsReadOnly => false;
    public int IndexOf(T item) => _list.IndexOf(item);

    public void Insert(int index, T item)
    {
        if (ObjectExists(item))
            return;
        _list.Insert(index, item);
    }

    public void RemoveAt(int index) => _list.RemoveAt(index);

    public T this[int index]
    {
        get { return _list[index]; }
        set
        {
            if (ObjectExists(value))
                return;
            _list[index] = value;
        }
    }

    private bool ObjectExists(T item) => _list.Any(i => ReferenceEquals(i, item));
}

然后我们可以测试我们的类

var person = new Person() { Id = 1 };
var person2 = new Person() { Id = 1 };

var listByReference = new ListByReference<Person>();

listByReference.Add(person);
listByReference.Add(person2);
listByReference.Add(person);

Console.WriteLine(listByReference.Count);
Console.WriteLine(listByReference.Contains(person));
Console.WriteLine(listByReference.Contains(person2));
Console.WriteLine(listByReference.Contains(new Person() { Id = 1 }));

输出是

2

是的

是的

错误

如您所见,我调用了 add 3 次,但名单上只有 2 个人,因为第一个已经添加。

当尝试添加一些已经存在的对象时,我选择不抛出异常,但您可以根据需要进行修改。

【讨论】:

  • 如果我不能编辑类本身,这不能使用。除非因为我把它包起来。一个结构包装器可能能够达到相同的效果......
  • @Alxandr 你可以编辑课程,就这样做values[1].Property = value
  • 例如,假设我需要通过身份(或我使用的框架的类)检查字符串。我无法向他们添加Id
  • @Alxandr 你能举个例子吗?
  • 如果没有被覆盖,== 将通过引用进行比较(如果变量指向同一个对象,则返回 true)。当然,对于引用类型(字符串除外)。 msdn.microsoft.com/en-us/library/53k8ybth.aspx
猜你喜欢
  • 2015-10-18
  • 2011-12-12
  • 1970-01-01
  • 2011-05-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-28
  • 1970-01-01
相关资源
最近更新 更多