What is the algorithm used by the memberwise equality test in .NET structs?

前端 未结 5 1442
再見小時候
再見小時候 2021-02-07 16:04

What is the algorithm used by the memberwise equality test in .NET structs? I would like to know this so that I can use it as the basis for my own algorithm.

I am trying

5条回答
  •  悲&欢浪女
    2021-02-07 16:16

    This is the implementation of ValueType.Equals from the Shared Source Common Language Infrastructure (version 2.0).

    public override bool Equals (Object obj) {
        BCLDebug.Perf(false, "ValueType::Equals is not fast.  "+
            this.GetType().FullName+" should override Equals(Object)");
        if (null==obj) {
            return false;
        }
        RuntimeType thisType = (RuntimeType)this.GetType();
        RuntimeType thatType = (RuntimeType)obj.GetType();
    
        if (thatType!=thisType) {
            return false;
        }
    
        Object thisObj = (Object)this;
        Object thisResult, thatResult;
    
        // if there are no GC references in this object we can avoid reflection 
        // and do a fast memcmp
        if (CanCompareBits(this))
            return FastEqualsCheck(thisObj, obj);
    
        FieldInfo[] thisFields = thisType.GetFields(
            BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
    
        for (int i=0; i

    It's interesting to note that this is pretty much exactly the code that is shown in Reflector. That suprised me because I thought that the SSCLI was just a reference implementation, not the final library. Then again, I suppose there is a limited number of ways to implement this relatively simple algorithm.

    The parts that I wanted to understand more are the calls to CanCompareBits and FastEqualsCheck. These are both implemented as native methods but their code is also included in the SSCLI. As you can see from the implementations below, the CLI looks at the definition of the object's class (via it's method table) to see if it contains pointers to reference types and how the memory for the object is laid out. If there are no references and the object is contiguous, then the memory is compared directly using the C function memcmp.

    // Return true if the valuetype does not contain pointer and is tightly packed
    FCIMPL1(FC_BOOL_RET, ValueTypeHelper::CanCompareBits, Object* obj)
    {
        WRAPPER_CONTRACT;
        STATIC_CONTRACT_SO_TOLERANT;
    
        _ASSERTE(obj != NULL);
        MethodTable* mt = obj->GetMethodTable();
        FC_RETURN_BOOL(!mt->ContainsPointers() && !mt->IsNotTightlyPacked());
    }
    FCIMPLEND
    
    FCIMPL2(FC_BOOL_RET, ValueTypeHelper::FastEqualsCheck, Object* obj1,
        Object* obj2)
    {
        WRAPPER_CONTRACT;
        STATIC_CONTRACT_SO_TOLERANT;
    
        _ASSERTE(obj1 != NULL);
        _ASSERTE(obj2 != NULL);
        _ASSERTE(!obj1->GetMethodTable()->ContainsPointers());
        _ASSERTE(obj1->GetSize() == obj2->GetSize());
    
        TypeHandle pTh = obj1->GetTypeHandle();
    
        FC_RETURN_BOOL(memcmp(obj1->GetData(),obj2->GetData(),pTh.GetSize()) == 0);
    }
    FCIMPLEND
    

    If I wasn't quite so lazy, I might look into the implementation of ContainsPointers and IsNotTightlyPacked. However, I've definitively find out what I wanted to know (and I am lazy) so that's a job for another day.

    • Shared Source Common Language Infrastructure 2.0

提交回复
热议问题