如果它们相等,是否可以比较两个托管引用(类型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
答案 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#中观察托管指针的唯一方法是在具有ref
或out
参数的函数内,但现在在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;
}
}
}
}