null 인 경우에도 두 "ref"변수가 동일한 변수를 참조하는지 확인하는 방법은 무엇입니까?
두 ref
변수가 모두 동일한 변수를 참조 하는지 여부를 어떻게 확인할 수 null
있습니까?
예:
public static void Main( string[] args )
{
object a = null;
object b = null;
Console.WriteLine( AreSame( ref a, ref b ) ); // Should print False
Console.WriteLine( AreSame( ref a, ref a ) ); // Should print True
}
static bool AreSame<T1, T2>( ref T1 a, ref T2 b )
{
// ?????
}
작동하지 않는 내가 시도한 것 :
return object.ReferenceEquals( a, b );
(두 테스트 케이스 모두에서 true를 반환)unsafe { return &a == &b; }
(관리 객체의 주소를 가져올 수 없음)
실제로 System.Runtime.CompilerServices.Unsafe 패키지 의 Unsafe.AreSame
메서드 를 사용할 수 있습니다 .
이것은 참조를 직접 비교하며 가장 깨끗한 솔루션입니다. 이 메서드는 IL로 작성되었으며 단순히 참조를 비교하기 때문에 IL에서 할 수 있습니다.
서로 다른 유형의 두 참조를 비교하려면 다음 오버로드를Unsafe.As
사용하여 그중 하나를 캐스팅 할 수 있습니다 .
static bool AreSame<T1, T2>(ref T1 a, ref T2 b)
=> Unsafe.AreSame(ref Unsafe.As<T1, T2>(ref a), ref b);
참조를 캐스팅하는 것이 복잡하다고 느끼는 경우 또 다른 제안 이 있습니다. 임의의 IL 코드를 C # 코드에 직접 삽입 할 수있는 InlineIL.Fody 라이브러리 를 사용하세요.
static bool AreSame<T1, T2>(ref T1 a, ref T2 b)
{
IL.Emit.Ldarg(nameof(a));
IL.Emit.Ldarg(nameof(b));
IL.Emit.Ceq();
return IL.Return<bool>();
}
제네릭을 만들 수없고 DynamicMethod
동적 형식을 생성해야하기 때문에 Reflection.Emit을 사용하여 런타임에 코드를 내보내는 것보다 쉽기 때문에 이것을 제안 합니다. IL 프로젝트를 작성할 수도 있지만 한 가지 방법에만 과도하게 느껴집니다.
또한 중요한 경우 외부 라이브러리에 대한 종속성을 피할 수 있습니다.
경쟁 조건의 가능성으로 인해 및 솔루션을 완전히 신뢰 하지는 않습니다 . 이러한 조건을 함께 모을만큼 불행한 경우 :__makeref
Unsafe.AsPointer
- 두 참조가 동일합니다.
- GC가 다른 스레드에 의해 트리거 된 후 상기 비교의 일측 평가되지만 전에 다른 하나는
- 관리되는 힙 어딘가에 참조 포인트
- 참조 된 객체는 힙 압축을 위해 GC에 의해 이동됩니다.
음, 이미 평가 된 포인터는 비교 전에 GC에서 업데이트되지 않으므로 잘못된 결과가 표시됩니다.
일어날 가능성이 있습니까? 별로. 그러나 그럴 수 있습니다.
이 Unsafe.AreSame
메서드는 항상 byref 공간에서 작동하므로 GC는 언제든지 참조를 추적하고 업데이트 할 수 있습니다.
안전하지 않은 코드와 문서화되지 않은 __makeref
방법을 사용하여 값을 수정하지 않고 방법이 있습니다.
public static void Main(string[] args)
{
object a = null;
object b = null;
Console.WriteLine(AreSame(ref a, ref b)); // prints False
Console.WriteLine(AreSame(ref a, ref a)); // prints True
}
static bool AreSame<T1, T2>(ref T1 a, ref T2 b)
{
TypedReference trA = __makeref(a);
TypedReference trB = __makeref(b);
unsafe
{
return *(IntPtr*)(&trA) == *(IntPtr*)(&trB);
}
}
참고 : 표현이 *(IntPtr*)(&trA)
사실에 의존 TypedReference의 첫 번째 필드가 입니다 IntPtr
우리가 비교하려는 변수를 가리키는. 안타깝게도 (또는 다행히?) 해당 필드에 액세스 할 수있는 관리 된 방법이 없습니다. 리플렉션을 사용하더라도 TypedReference
상자에 넣을 수 없으므로 .NET과 함께 사용할 수 없습니다 FieldInfo.GetValue
.
참조를 임시 변수로 변경하고 다른 변수도 변경되는지 확인하여이 작업을 수행 할 수 있습니다.
빠른 테스트를했는데 작동하는 것 같습니다.
static bool AreSame(ref object a, ref object b) {
var old_a = a;
a = new object();
bool result = object.ReferenceEquals(a, b);
a = old_a;
return result;
}
static void Main(string[] args) {
object a = null;
object b = null;
var areSame1 = AreSame(ref a, ref b); // returns false
var areSame2 = AreSame(ref a, ref a); // returns true
}
문서화되지 않은 __makeref
키워드를 사용하지 않는 또 다른 솔루션이 있습니다 . 그러면 System.Runtime.CompilerServices.Unsafe NuGet 패키지 가 사용 됩니다.
using System.Runtime.CompilerServices;
public static void Main( string[] args )
{
object a = null;
object b = null;
Console.WriteLine( AreSame( ref a, ref b ) ); // Prints False
Console.WriteLine( AreSame( ref a, ref a ) ); // Prints True
}
unsafe static bool AreSame<T1, T2>( ref T1 a, ref T2 b )
{
var pA = Unsafe.AsPointer( ref a );
var pB = Unsafe.AsPointer( ref b );
return pA == pB;
}
'IT이야기' 카테고리의 다른 글
ImportError : concurrent.futures.process라는 모듈이 없습니다. (0) | 2021.03.26 |
---|---|
React Component에서 이미지 (.svg, .png)를 가져 오는 방법 (0) | 2021.03.26 |
"show tables"MySQL 쿼리에서 데이터 선택 (0) | 2021.03.26 |
순환 복잡성이 유용한 측정 (0) | 2021.03.25 |
R에서 행 인덱스 번호를 얻는 방법 (0) | 2021.03.25 |