比较两个托管引用

时间:2014-11-16 10:32:20

标签: c# .net

如果它们相等,是否可以比较两个托管引用(类型ref T)?我不是指对象的引用,而是对变量的引用。例如:

public static bool Compare(ref int a, ref int b)
{
    return ref a == ref b; //something like that, not possible this way in C#
}

int x, y;
Compare(ref x, ref x); //true
Compare(ref x, ref y); //false

5 个答案:

答案 0 :(得分:3)

这里有明确的参考(没有双关语) - Equality Comparisons (C# Programming Guide)

您可以使用Object.ReferenceEquals来比较两个类型为T的对象,以便{和我只知道,T是引用类型。

正如Haedrian指出的那样is not possible for value types even when they are passed by reference由于拳打ReferenceEquals而引起的。

int x = 0, y = 0;
IsSameReference(ref x, ref x).Dump(); // Passing the same value type variable twice, by reference. We want a result of 'true'
IsSameReference(ref x, ref y).Dump(); // We expect 'false'

public static bool IsSameReference(ref int a, ref int b)
{
    return Object.ReferenceEquals(a, b);
}

两次调用转储false。 (注意我重命名了函数Compare,因为它通常用于排序比较)。

基本上,T可以是任何类型,答案是否定的。

(带有int的趣味和游戏只能从另一个答案取代的答案中删除)。

答案 1 :(得分:1)

Pointer comparing有效,但不是通用的。幸运的是,TypedReference适用于泛型,但如果没有一点内存读取,就没有办法比较它。虽然目前有效,但不保证这项技术将来会有效。

public static unsafe bool Equals(this TypedReference tr, TypedReference other)
{
    IntPtr* a = ((IntPtr*)&tr);
    IntPtr* b = ((IntPtr*)&other);
    return a[0] == b[0] && a[1] == b[1];
}

public static bool Equals<T>(ref T a, ref T b)
{
    return __makeref(a).Equals(__makeref(b));
}

答案 2 :(得分:1)

根据建议here,这是一个快速有效的测试两个托管指针(参考)相等的版本。该函数对.NET 值类型实例最有用(即struct而不是class):它将返回 true 当且仅当两个托管引用指向GC堆中的相同“位置”时(引号标记为提醒管理指针的整个点是它们“跟踪”GC对象并且即使它们的物理地址发生更改仍保持有效):

  

public static bool IL&lt; T&gt; .RefEquals(ref T a,ref T b) / *下面提供的代码* /

在C#7之前,在C#中观察托管指针的唯一方法是在具有refout参数的函数内,但现在在2017年新版本的语言允许您可以显式声明托管指针 - 遵循某些条件 - 作为本地或函数返回值。

对于引用类型,这种引用相等形式将应用于引用句柄本身,使其成为比Object.ReferenceEquals更严格的约束。换句话说,不仅比较器必须引用相同的GC对象,而且它们必须通过完全相同的引用句柄来实现。

代码仅使用DynamicMethod四条IL指令,但必须在运行时执行一次性启动方法。首先,完整的课程。这是您需要的唯一部分。测试和演示代码如下。

public static class IL<T>
{
    public delegate bool delRefEq(ref T a, ref T b);

    public static readonly delRefEq RefEquals;   <--- here you go

    static IL()
    {
        var dm = new DynamicMethod(
            "__IL_RefEquals<" + typeof(T).FullName + ">",
            typeof(bool),
            new[] { typeof(T).MakeByRefType(), typeof(T).MakeByRefType() },
            typeof(Object),
            true);

        var il = dm.GetILGenerator();
        il.Emit(OpCodes.Ldarg_0);
        il.Emit(OpCodes.Ldarg_1);
        il.Emit(OpCodes.Ceq);
        il.Emit(OpCodes.Ret);

        RefEquals = (delRefEq)dm.CreateDelegate(typeof(delRefEq));
    }
};

这就是你所需要的一切。以下是一些简单的演示代码供使用。随着ref locals现在在C#7中的出现,我预计上述功能将会得到很多用途。

static unsafe class ref_eq_test
{
    public static bool RefEquals<T>(ref T a, ref T b) => IL<T>.RefEquals(ref a, ref b);

    public static void Test1(Guid g) => Test2(g, ref g, &g);

    static void Test2(Guid g, ref Guid r0, Guid* p0)
    {
        Guid g2 = g;
        ref Guid r2 = ref g;                        // <-- C# 7 only
        ref Guid r3 = ref r0;                       // <-- C# 7 only

        check(RefEquals(ref g, ref g), true);       // identity of values
        check(RefEquals(ref g, ref g2), false);     // not same reference
        check(RefEquals(ref g, ref r0), false);     // as set by caller
        check(RefEquals(ref g, ref *p0), false);    // 'g' is not 'ref'
        check(RefEquals(ref r0, ref r0), true);     // identity of value refs
        check(RefEquals(ref r0, ref *p0), true);    // mixing ref with pointer
        check(RefEquals(ref *p0, ref *p0), true);   // identity of pointer deref
        check(RefEquals(ref r2, ref g), true);      // more cases...
        check(RefEquals(ref r2, ref g2), false);    // 'g2' is not a ref
        check(RefEquals(ref r3, ref *p0), true);    // transitive ref chain
    }

    static void check(bool _exp, bool _act) { if (_exp != _act) throw new Exception(); }
};

答案 3 :(得分:1)

您可以使用官方的Microsoft NuGet软件包<meta charset="utf-8" />来完成此操作,而不必将您的方法标记为System.Runtime.CompilerServices.Unsafe

unsafe

请注意,我认为这种方法不是真正的using System.Runtime.CompilerServices; static void Main() { int value = 0; ref int ref1 = ref value; ref int ref2 = ref value; Debug.Assert(Unsafe.AreSame(ref ref1, ref ref2)); } ,也许应该与Unsafe一起移到另一个类/程序包中。

答案 4 :(得分:0)

此答案适用于int类型,不需要查看TypedReference对象的内部结构。

    static bool ReferenceEquals(ref int a, ref int b)
    {
        unsafe
        {
            fixed (int* pA = &a)
            {
                fixed (int* pB = &b)
                {
                    return pA == pB;
                }
            }
        }
    }