什么是用于在.NET结构的成员逐一相等测试的算法?算法、成员、结构、测试

2023-09-03 06:42:30 作者:独一无二的少年

什么是由在.NET结构的成员逐一相等测试的算法?我想知道,这样我可以用它为基础,我自己的算法。

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.

我想写一个递归成员逐一平等检验任意对象(在C#)用于测试的DTO的逻辑平等。这是相当容易,如果DTO的是结构(自ValueType.Equals大多数时候是正确的事),但并不总是合适的。我还想重写任何IEnumerable的对象比较(而不是字符串!),使它们的内容进行比较,而不是它们的属性。

I am trying to write a recursive memberwise equality test for arbitrary objects (in C#) for testing the logical equality of DTOs. This is considerably easier if the DTOs are structs (since ValueType.Equals does mostly the right thing) but that is not always appropriate. I would also like to override comparison of any IEnumerable objects (but not strings!) so that their contents are compared rather than their properties.

这已被证明是难度比我期望的那样。任何提示将大大AP preciated。我会接受,证明最有用的还是提供了一个链接到最有用的信息的答案。

This has proven to be harder than I would expect. Any hints will be greatly appreciated. I'll accept the answer that proves most useful or supplies a link to the most useful information.

感谢。

推荐答案

这是 ValueType.Equals 从共享源代码公共语言基础设施(2.0版)的实施。

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<thisFields.Length; i++) {
        thisResult = ((RtFieldInfo)thisFields[i])
            .InternalGetValue(thisObj, false);
        thatResult = ((RtFieldInfo)thisFields[i])
            .InternalGetValue(obj, false);

        if (thisResult == null) {
            if (thatResult != null)
                return false;
        }
        else
        if (!thisResult.Equals(thatResult)) {
            return false;
        }
    }

    return true;
}

这是有趣的是,这是pretty的多正是显示在反射器的code。那惊讶我,因为我认为SSCLI只是一个参考实现,而不是最终的库。再说,我想有一种方式来实现这个相对简单的算法是有限的。

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.

这是我想了解更多的部分是调用 CanCompareBits FastEqualsCheck 。这些都被实现为本地方法但其code为也包括在SSCLI。正如可以从下面的实现中可以看到,在CLI着眼于该对象的类的定义(通过它的方法表),以查看它是否包含指针引用类型以及如何为对象的存储器被布置。如果没有引用和对象是连续的,那么内存相比直接使用C函数 memcmp

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

如果我不是这么懒,我可能会考虑的 ContainsPointers实施 IsNotTightlyPacked 。不过,我已经明确地找出我想知道(我的(上午)的懒惰),所以这是另一天的工作。

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.

共享源代码公共语言基础2.0