Microsoft MVP성태의 닷넷 이야기
글쓴 사람
정성태 (techsharer at outlook.com)
홈페이지
첨부 파일
(연관된 글이 15개 있습니다.)

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>를 위해 도입한 문법일 것이기 때문입니다.

(첨부 파일은 이 글의 예제를 포함합니다.)




[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]

[연관 글]






[최초 등록일: ]
[최종 수정일: 6/11/2018]

Creative Commons License
이 저작물은 크리에이티브 커먼즈 코리아 저작자표시-비영리-변경금지 2.0 대한민국 라이센스에 따라 이용하실 수 있습니다.
by SeongTae Jeong, mailto:techsharer at outlook.com

비밀번호

댓글 작성자
 



2018-05-31 11시32분
[spowner] 속도 개선이 주 목적일 텐데, 강제가 아닌 예약어를 사용해야 개선된다는 점은 아쉽네요.
[guest]
2018-06-01 12시09분
ref struct 자체는 속도 개선의 목적은 아니고 Span/TypeReference와 같은 특수 유형의 타입을 점차로 양지로 끌어오는 과정의 하나로 보입니다. 저걸 강제화시키면 그 동안 작성했던 모든 struct 타입들의 사용이 컴파일 오류가 발생하게 됩니다.
정성태

... 121  122  123  124  125  126  127  128  129  130  131  132  133  [134]  135  ...
NoWriterDateCnt.TitleFile(s)
1739정성태8/24/201427752.NET Framework: 457. 교착상태(Dead-lock) 해결 방법 - Lock Leveling [2]파일 다운로드1
1738정성태8/23/201423444.NET Framework: 456. C# - CAS를 이용한 Lock 래퍼 클래스파일 다운로드1
1737정성태8/20/201420927VS.NET IDE: 93. Visual Studio 2013 동기화 문제
1736정성태8/19/201426916VC++: 79. [부연] CAS Lock 알고리즘은 과연 빠른가? [2]파일 다운로드1
1735정성태8/19/201419409.NET Framework: 455. 닷넷 사용자 정의 예외 클래스의 최소 구현 코드 - 두 번째 이야기
1734정성태8/13/201421168오류 유형: 237. Windows Media Player cannot access the file. The file might be in use, you might not have access to the computer where the file is stored, or your proxy settings might not be correct.
1733정성태8/13/201427509.NET Framework: 454. EmptyWorkingSet Win32 API를 사용하는 C# 예제파일 다운로드1
1732정성태8/13/201435822Windows: 99. INetCache 폴더가 다르게 보이는 이유
1731정성태8/11/201428297개발 환경 구성: 235. 점(.)으로 시작하는 파일명을 탐색기에서 만드는 방법
1730정성태8/11/201423480개발 환경 구성: 234. Royal TS의 터미널(Terminal) 연결에서 한글이 깨지는 현상 해결 방법
1729정성태8/11/201419470오류 유형: 236. SqlConnection - The requested Performance Counter is not a custom counter, it has to be initialized as ReadOnly.
1728정성태8/8/201431710.NET Framework: 453. C# - 오피스 파워포인트(Powerpoint) 파일을 WinForm에서 보는 방법파일 다운로드1
1727정성태8/6/201421907오류 유형: 235. SignalR 오류 메시지 - Counter 'Messages Bus Messages Published Total' does not exist in the specified Category. [2]
1726정성태8/6/201420705오류 유형: 234. IIS Express에서 COM+ 사용 시 SecurityException - "Requested registry access is not allowed" 발생
1725정성태8/6/201422654오류 유형: 233. Visual Studio 2013 Update3 적용 후 Microsoft.VisualStudio.Web.PageInspector.Runtime 모듈에 대한 FileNotFoundException 예외 발생
1724정성태8/5/201427450.NET Framework: 452. .NET System.Threading.Thread 개체에서 Native Thread Id를 구하는 방법 - 두 번째 이야기 [1]파일 다운로드1
1723정성태7/29/201459848개발 환경 구성: 233. DirectX 9 예제 프로젝트 빌드하는 방법 [3]파일 다운로드1
1722정성태7/25/201422193오류 유형: 232. IIS 500 Internal Server Error - NTFS 암호화된 폴더에 웹 애플리케이션이 위치한 경우
1721정성태7/24/201425495.NET Framework: 451. 함수형 프로그래밍 개념 - 리스트 해석(List Comprehension)과 순수 함수 [2]
1720정성태7/23/201423459개발 환경 구성: 232. C:\WINDOWS\system32\LogFiles\HTTPERR 폴더에 로그 파일을 남기지 않는 설정
1719정성태7/22/201427346Math: 13. 동전을 여러 더미로 나누는 경우의 수 세기(Partition Number) - 두 번째 이야기파일 다운로드1
1718정성태7/19/201436791Math: 12. HTML에서 수학 관련 기호/수식을 표현하기 위한 방법 - MathJax.js [4]
1716정성태7/17/201436488개발 환경 구성: 231. PC 용 무료 안드로이드 에뮬레이터 - genymotion
1715정성태7/13/201431578기타: 47. 운영체제 종료 후에도 USB 외장 하드의 전원이 꺼지지 않는 경우 [3]
1714정성태7/11/201421572VS.NET IDE: 92. Visual Studio 2013을 지원하는 IL Support 확장 도구
1713정성태7/11/201445345Windows: 98. 윈도우 시스템 디스크 용량 확보를 위한 "Package Cache" 폴더 이동 [1]
... 121  122  123  124  125  126  127  128  129  130  131  132  133  [134]  135  ...