contains 方法


不管在c#中還是java中,很多類型都有contains方法。它的原理是什么?

看一個java的例子

http://blog.csdn.net/fwwdn/article/details/6746849

 

c#中的contains有:

String.Contains
List(T).Contains
Enumerable.Contains(TSource)
Vector.contains
Queue(T).Contains
Enumerable.Contains
Collection.contains
HashSet(T).Contains
ICollection(T).Contains
Array.contains
IQueryable(T).Contains
Hashtable.Contains
ArrayList.contains
Collection(T).Contains
Enumerable.Contains(TSource)
EntityCollection(TEntity).Contains
Stack(T).Contains
SortedList.Contains
PropertyBag.Contains

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

等等。。。。。。。。。

 

看了下msdn,這些contains的原理,大致分三種

1,默認相等比較器

2,equals

3,hashcode

以List<T> 為例,其contains方法的定義為

// System.Collections.Generic.List<T>
/// <summary>Determines whether an element is in the <see cref="T:System.Collections.Generic.List`1" />.</summary>
/// <returns>true if <paramref name="item" /> is found in the <see cref="T:System.Collections.Generic.List`1" />; otherwise, false.</returns>
/// <param name="item">The object to locate in the <see cref="T:System.Collections.Generic.List`1" />. The value can be null for reference types.</param>
[__DynamicallyInvokable]
public bool Contains(T item)
{
    if (item == null)
    {
        for (int i = 0; i < this._size; i++)
        {
            if (this._items[i] == null)
            {
                return true;
            }
        }
        return false;
    }
    EqualityComparer<T> @default = EqualityComparer<T>.Default;
    for (int j = 0; j < this._size; j++)
    {
        if (@default.Equals(this._items[j], item))
        {
            return true;
        }
    }
    return false;
}

可以看到,使用了默認相等比較器的方法Equals:EqualityComparer,看一下其定義

http://msdn.microsoft.com/zh-cn/library/ms224763(v=vs.110).aspx

public abstract class EqualityComparer<T> : IEqualityComparer, IEqualityComparer<T>

這兩個接口只有兩個方法,Equals和GetHashCode,但是EqualityComparer是一個抽象類,它實現了IEqualityComparer接口的兩個方法且定義為私有方法,卻把IEqualityComparer<T>接口的兩個方法實現為了抽象方法。

那么因為@default.Equals調用的是IEqualityComparer<T>接口的方法,所以我們要知道Equals的實現方式,關鍵是在Default的獲取上,通過它才可以看到Equals的定義,看一下Default的定義

        public static EqualityComparer<T> Default
        {
            get
            {
                EqualityComparer<T> equalityComparer = EqualityComparer<T>.defaultComparer;
                if (equalityComparer == null)
                {
                    equalityComparer = EqualityComparer<T>.CreateComparer();
                    EqualityComparer<T>.defaultComparer = equalityComparer;
                }
                return equalityComparer;
            }
        }

這里邊defaultComparer的定義:(todo volatile 修飾符的定義

private static volatile EqualityComparer<T> defaultComparer;

我們暫時當它是null的,那么就會調用私有方法CreateComparer了。

private static EqualityComparer<T> CreateComparer()
        {
            RuntimeType runtimeType = (RuntimeType)typeof(T);
            if (runtimeType == typeof(byte))
            {
                return (EqualityComparer<T>)new ByteEqualityComparer();
            }
            if (typeof(IEquatable<T>).IsAssignableFrom(runtimeType))
            {
                return (EqualityComparer<T>)RuntimeTypeHandle.CreateInstanceForAnotherGenericParameter((RuntimeType)typeof(GenericEqualityComparer<int>), runtimeType);
            }
            if (runtimeType.IsGenericType && runtimeType.GetGenericTypeDefinition() == typeof(Nullable<>))
            {
                RuntimeType runtimeType2 = (RuntimeType)runtimeType.GetGenericArguments()[0];
                if (typeof(IEquatable<>).MakeGenericType(new Type[]
                {
                    runtimeType2
                }).IsAssignableFrom(runtimeType2))
                {
                    return (EqualityComparer<T>)RuntimeTypeHandle.CreateInstanceForAnotherGenericParameter((RuntimeType)typeof(NullableEqualityComparer<int>), runtimeType2);
                }
            }
            if (runtimeType.IsEnum && Enum.GetUnderlyingType(runtimeType) == typeof(int))
            {
                return (EqualityComparer<T>)RuntimeTypeHandle.CreateInstanceForAnotherGenericParameter((RuntimeType)typeof(EnumEqualityComparer<int>), runtimeType);
            }
            return new ObjectEqualityComparer<T>();
        }

在這里,根據不同的運行時類型,實例化不同的EqualityComparer子類。在此只看最后一個return(todo 其他子類),ObjectEqualityComparer:這是一個internal的類,我們是用不了的

internal class ObjectEqualityComparer<T> : EqualityComparer<T>

然后我們可以找到Equals,這里會有兩個

        public override bool Equals(T x, T y)
        {
            if (x != null)
            {
                return y != null && x.Equals(y);
            }
            return y == null;
        }
        public override bool Equals(object obj)
        {
            ObjectEqualityComparer<T> objectEqualityComparer = obj as ObjectEqualityComparer<T>;
            return objectEqualityComparer != null;
        }

我們用到的是第一個(todo,第二個)。我們看到,這里又一次調用了Equals,但是這個Equals很好定位,它就是Object.Equals

如果當前實例是引用類型,Equals(Object) 方法測試引用相等性,並且,對於 Equals(Object) 方法的調用等效於 ReferenceEquals 方法的調用。 引用相等性意味着進行比較的對象變量引用同一個對象。如果當前實例是值類型,Equals(Object) 方法測試值相等性。 

派生類通常重寫 Object.Equals(Object) 方法實現值相等性。 此外,類型通常還提供其他的重載到 Equals 方法的強類型,通常通過實現 IEquatable<T> 接口。 當您調用 Equals 方法測試是否相等時,應知道是否當前實例重寫 Object.Equals 並了解如何解決對 Equals 方法的特定調用。 否則,您可以執行與您預期不同的相等測試,因此,方法可能會返回意外的值。

http://msdn.microsoft.com/zh-cn/library/bsc2ak47.aspx

追下去后:

public virtual bool Equals(object obj)
{
    return RuntimeHelpers.Equals(this, obj);
}
[SecuritySafeCritical]
[MethodImpl(MethodImplOptions.InternalCall)]
public new static extern bool Equals(object o1, object o2);

我覺得系統內置的類的Equals方法,都會重寫的。我們關心的是我們自定義的類是怎么使用Equals的。但是這里開始導入外部的方法了,再進一步不知道該怎么辦了(todo 繼續追查,extern修飾符的定義)。

http://blog.csdn.net/llddyy123wq/article/details/5620466

--======================================================================================

這條路走不通,換一個類型,試試HashSet<T>,首先找到它的contains方法

public bool Contains(T item)
{
    if (this.m_buckets != null)
    {
        int num = this.InternalGetHashCode(item);
        for (int i = this.m_buckets[num % this.m_buckets.Length] - 1; i >= 0; i = this.m_slots[i].next)
        {
            if (this.m_slots[i].hashCode == num && this.m_comparer.Equals(this.m_slots[i].value, item))
            {
                return true;
            }
        }
    }
    return false;
}

看了下,m_comparer是IEqualityComparer<T>類型的,所以這里的Equals和上邊是一樣的。除了Equals,hashset還要比較hashcode,這是比較特別的地方。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM