C# - 구조체(값 형식)의 필드를 리플렉션을 이용해 값을 바꾸는 방법
다음은 구조체(struct)의 필드를 리플렉션(Reflection)으로 값을 설정하기 위해 작성한 코드입니다.
using System;
using System.Reflection;
namespace ConsoleApp1
{
class Program
{
static void Main(string[] args)
{
MyStruct ms = new MyStruct();
ulong m = 5;
FieldInfo fi = typeof(MyStruct).GetField("dt", BindingFlags.Instance | BindingFlags.NonPublic);
fi.SetValue(ms, m);
Console.WriteLine(ms);
}
}
public struct MyStruct
{
private ulong dt;
public override string ToString()
{
return dt.ToString();
}
}
}
실행해 보면 알겠지만, 출력은 "0"이 나옵니다. 이유는 간단합니다. 구조체와 같은 값 형식의 변수는 메서드에 인자로 전달할 시, 스택을 통한 값 복사가 발생하기 때문입니다. 결국 스택에 잠시 생성되는 인스턴스의 값이 변하는 것에 불과한 것입니다.
마이크로소프트는 이에 대한 문제를 진작에 인식한 것으로 보입니다. 검색해 보면,
FieldInfo.SetValue don't work in struct
; https://social.msdn.microsoft.com/Forums/vstudio/en-US/33284e33-d004-4b76-bc0f-50100ec46bf1/fieldinfosetvalue-dont-work-in-struct?forum=csharpgeneral
TypedReference Structure
; https://learn.microsoft.com/en-us/dotnet/api/system.typedreference
__makeref 예약어와 함께 .NET 1.1 BCL부터 존재하던 TypedReference 형식을 인자로 받는 FieldInfo.
SetValueDirect 메서드를 이용해 다음과 같이 해결할 수 있습니다.
{
MyStruct ms = new MyStruct();
ulong m = 5;
FieldInfo fi = typeof(MyStruct).GetField("dt", BindingFlags.Instance | BindingFlags.NonPublic);
TypedReference tr = __makeref(ms);
fi.SetValueDirect(tr, m);
Console.WriteLine(ms); // 출력 결과: 5
}
TypedReference는 관리 환경의 포인터 격에 해당하는데, 여기서 재미있는 점은 바로 C#의 비공식 키워드로 존재하는 "__makeref"입니다. 마이크로소프트 측에서 이런 비공식 예약어들(__arglist, __refvalue, __reftype)을 얼마나 숨기고 싶어 하는지... 구글로 검색해도 msdn 같은 도움말을 통한 공식 자료가 전혀 노출되고 있지 않다는 것입니다.
암튼, 어차피 값 형식의 경우 주솟값으로 접근해야 한다는 점이 중요합니다. 이런 점에 착안해 다음과 같이 포인터 연산을 통해서도 필드의 값을 바꾸는 것도 가능합니다.
{
MyStruct ms = new MyStruct();
ulong* p = (ulong *)&ms;
{
*p = 5;
}
Console.WriteLine(ms); // 출력 결과: 5
}
그러고 보니, 언젠가 dynamic 예약어와 관련했던 글이 하나 생각나는군요. ^^
C# - 클래스 안에 구조체를 포함하는 경우 발생하는 dynamic 키워드의 부작용
; https://www.sysnet.pe.kr/2/0/1323
(
첨부 파일은 이 글의 예제 코드를 포함합니다.)
[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]