C# 7.2 - 스택에만 생성할 수 있는 값 타입 지원 - "ref struct"
C# 7.2 (1) - readonly 구조체
; https://www.sysnet.pe.kr/2/0/11524
C# 7.2 (2) - 메서드의 매개 변수에 in 변경자 추가
; https://www.sysnet.pe.kr/2/0/11525
C# 7.2 (3) - 메서드의 반환값 및 로컬 변수에 ref readonly 기능 추가
; https://www.sysnet.pe.kr/2/0/11526
C# 7.2 (4) - 3항 연산자에 ref 지원(conditional ref operator)
; https://www.sysnet.pe.kr/2/0/11528
C# 7.2 (5) - 스택에만 생성할 수 있는 값 타입 지원 - "ref struct"
; https://www.sysnet.pe.kr/2/0/11530
C# 7.2 (6) - Span<T>
; https://www.sysnet.pe.kr/2/0/11534
C# 7.2 (7) - private protected 접근자 추가
; https://www.sysnet.pe.kr/2/0/11543
C# 7.2 (8) - 숫자 리터럴의 선행 밑줄과 뒤에 오지 않는 명명된 인수
; https://www.sysnet.pe.kr/2/0/11544
기타 - Microsoft Build 2018 - The future of C# 동영상 내용 정리
; https://www.sysnet.pe.kr/2/0/11536
지난 글에서,
닷넷의 관리 포인터(Managed Pointer)와 System.TypedReference
; https://www.sysnet.pe.kr/2/0/11529
설명한 System.TypedReference는 내부에 "관리 포인터"를 가지고 있으며, 그 특성으로 인해 관리 힙에 놓일 수 없는 타입이라고 했습니다. 이렇게 "스택에만 생성할 수 있는 타입"을 정의할 수 있는 C# 문법이 바로 7.2부터 제공되는 "ref struct"입니다.
C# 7 Series, Part 9: ref structs
; https://blogs.msdn.microsoft.com/mazhou/2018/03/02/c-7-series-part-9-ref-structs/
ref struct
; https://docs.microsoft.com/ko-kr/dotnet/csharp/reference-semantics-with-value-types
Compile time enforcement of safety for ref-like types [7.2 Proposal]
; https://www.infoq.com/news/2017/06/CSharp-7.2
Compile time enforcement of safety for ref-like types
; https://github.com/VSadov/csharplang/blob/ef68acb505ad1a3310de133ef7af65c2c24da520/proposals/span-safety.md
(위의 링크에서 제목 중에 "ref-like types"라고 불리는 것이 "ref struct"입니다.)
사용 방법은 단순히 "ref struct" 예약어로 타입을 정의하는 식입니다.
ref struct Vector
{
public int X;
public int Y;
public int Z;
public void Output()
{
Console.WriteLine($"{X},{Y},{Z}");
}
}
"스택에만 생성할 수 있는 타입"이므로 로컬 변수와 매개 변수에 사용할 수 있고, TypedReference와 달리 내부에 "관리 포인터"를 가지고 있지는 않으므로 변수의 반환에서도 (struct의 값 복사 특성으로 인해) "로컬 변수"인 경우 가능합니다.
using System;
ref struct Vector
{
public int X;
public int Y;
public int Z;
}
class Program
{
// 컴파일 오류: CS8345 Field or auto-implemented property cannot be of type 'Vector' unless it is an instance member of a ref struct.
// Vector vector; // 타입의 멤버는 불가능
static void Main(string[] args)
{
// 로컬 변수로 스택에 생성되므로 OK!
Vector v = new Vector { X = 0, Y = 0, Z = 0 };
v.X = 50;
Console.WriteLine(v.X);
PassVector(v);
PassVectorIn(in v);
}
private static void PassVectorIn(in Vector v) // in 매개 변수로 가능
{
}
private static void PassVector(Vector v) // 매개 변수로 가능 (스택에 복사되어 전달되므로.)
{
}
private static Vector GetVector()
{
Vector v = new Vector { X = 1, Y = 2, Z = 3 };
return v; // 로컬 변수의 반환으로도 가능
}
}
스택에만 생성할 수 있다는 제약으로 인해 당연히 다른 "ref struct"의 필드로는 정의할 수 있습니다.
ref struct Matrix3x3
{
public Vector v1;
public Vector v2;
public Vector v3;
}
또한 메서드도 정의할 수 있지만,
static void Main(string[] args)
{
Vector v = new Vector { X = 0, Y = 0, Z = 0 };
v.Output();
}
ref struct Vector
{
public int X;
public int Y;
public int Z;
public void Output()
{
Console.WriteLine($"{X},{Y},{Z}");
}
}
오직 자신이 정의한 메서드만 사용할 수 있고, 그 외의 (박싱이 발생해 관리 힙으로 올라갈 여지가 있는) 모든 메서드를 사용할 수 없습니다.
Vector v = new Vector { X = 0, Y = 0, Z = 0 };
// 컴파일 오류: CS0029 Cannot implicitly convert type 'ret_struct.Vector' to 'object'
v.GetType();
// 컴파일 오류: CS0029 Cannot implicitly convert type 'reflike.Vector' to 'System.ValueType'
v.ToString();
// 컴파일 오류: CS0029 Cannot implicitly convert type 'reflike.Vector' to 'System.ValueType'
v.Equals(null);
// 컴파일 오류: CS0029 Cannot implicitly convert type 'reflike.Vector' to 'System.ValueType'
v.GetHashCode();
물론, 위의 메서드 중 ToString, Equals, GetHashCode는 ref struct 타입 내에서 재정의(override)하면 사용 가능합니다.
개인적으로 처음 ref struct를 봤을 때 System.TypedReference와 같은 타입을 이제 C#으로도 정의할 수 있게 길을 열어주는 것이 아닌가 생각했었습니다. 하지만, 여전히 ref struct 타입도 관리 포인터를 내부에 정의할 수 없는 제약을 가지고 있습니다.
ref struct IncludeManagedPointer
{
// 컴파일 에러 - CS0501 'IncludeManagedPointer.Age()' must declare a body because it is not marked abstract, extern, or partial ref_struct
public ref int Age;
}
따라서, ref struct는 스택에만 할당할 수 있는 특별한 타입 정의에 불과합니다.
ref struct 구조체 역시 readonly 구조체를 지원합니다.
C# 7.2 - readonly 구조체
; https://www.sysnet.pe.kr/2/0/11524
readonly ref struct Vector
{
public readonly int X;
public readonly int Y;
public readonly int Z;
public Vector(int x, int y, int z)
{
X = x;
Y = y;
Z = z;
}
public void Output()
{
Console.WriteLine($"{X},{Y},{Z}");
}
}
그건 그렇다 치고... 그나저나 이런 타입을 도대체 어디다 써먹어야 할까요? ^^ 사실, 일반 개발자들에게는 거의 잊고 지내도 좋을 문법입니다. 아마도 이것은 마이크로소프트가 Span<T>를 위해 도입한 문법일 것이기 때문입니다.
(
첨부 파일은 이 글의 예제를 포함합니다.)
[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]