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

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 타입들의 사용이 컴파일 오류가 발생하게 됩니다.
정성태

... 31  32  33  34  35  36  37  38  39  [40]  41  42  43  44  45  ...
NoWriterDateCnt.TitleFile(s)
12620정성태4/29/202111923.NET Framework: 1052. C# - 왜 구조체는 16 바이트의 크기가 적합한가? [1]파일 다운로드1
12619정성태4/28/202112424.NET Framework: 1051. C# - 구조체의 크기가 16바이트가 넘어가면 힙에 할당된다? [2]파일 다운로드1
12618정성태4/27/202110986사물인터넷: 58. NodeMCU v1 ESP8266 CP2102 Module을 이용한 WiFi UDP 통신 [1]파일 다운로드1
12617정성태4/26/20218644.NET Framework: 1050. C# - ETW EventListener의 Keywords별 EventId에 따른 필터링 방법파일 다운로드1
12616정성태4/26/20218616.NET Framework: 1049. C# - ETW EventListener를 상속받았을 때 초기화 순서파일 다운로드1
12615정성태4/26/20216788오류 유형: 712. Microsoft Live 로그인 - 계정을 선택하는(Pick an account) 화면에서 진행이 안 되는 문제
12614정성태4/24/20219464개발 환경 구성: 570. C# - Azure AD 인증을 지원하는 ASP.NET Core/5+ 웹 애플리케이션 예제 구성 [4]파일 다운로드1
12613정성태4/23/20218541.NET Framework: 1048. C# - ETW 이벤트의 Keywords에 속한 EventId 구하는 방법 (2) 관리 코드파일 다운로드1
12612정성태4/23/20218636.NET Framework: 1047. C# - ETW 이벤트의 Keywords에 속한 EventId 구하는 방법 (1) PInvoke파일 다운로드1
12611정성태4/22/20217941오류 유형: 711. 닷넷 EXE 실행 오류 - Mixed mode assembly is build against version 'v2.0.50727' of the runtime
12610정성태4/22/20217740.NET Framework: 1046. C# - 컴파일 시점에 참조할 수 없는 타입을 포함한 이벤트 핸들러를 Reflection을 이용해 구독하는 방법파일 다운로드1
12609정성태4/22/20219019.NET Framework: 1045. C# - 런타임 시점에 이벤트 핸들러를 만들어 Reflection을 이용해 구독하는 방법파일 다운로드1
12608정성태4/21/202110044.NET Framework: 1044. C# - Generic Host를 이용해 .NET 5로 리눅스 daemon 프로그램 만드는 방법 [9]파일 다운로드1
12607정성태4/21/20218572.NET Framework: 1043. C# - 실행 시점에 동적으로 Delegate 타입을 만드는 방법파일 다운로드1
12606정성태4/21/202112460.NET Framework: 1042. C# - enum 값을 int로 암시적(implicit) 형변환하는 방법? [2]파일 다운로드1
12605정성태4/18/20218533.NET Framework: 1041. C# - AssemblyID, ModuleID를 관리 코드에서 구하는 방법파일 다운로드1
12604정성태4/18/20217314VS.NET IDE: 163. 비주얼 스튜디오 속성 창의 "Build(빌드)" / "Configuration(구성)"에서의 "활성" 의미
12603정성태4/16/20218154VS.NET IDE: 162. 비주얼 스튜디오 - 상속받은 컨트롤이 디자인 창에서 지원되지 않는 문제
12602정성태4/16/20219351VS.NET IDE: 161. x64 DLL 프로젝트의 컨트롤이 Visual Studio의 Designer에서 보이지 않는 문제 [1]
12601정성태4/15/20218456.NET Framework: 1040. C# - REST API 대신 github 클라이언트 라이브러리를 통해 프로그래밍으로 접근
12600정성태4/15/20218638.NET Framework: 1039. C# - Kubeconfig의 token 설정 및 인증서 구성을 자동화하는 프로그램
12599정성태4/14/20219339.NET Framework: 1038. C# - 인증서 및 키 파일로부터 pfx/p12 파일을 생성하는 방법파일 다운로드1
12598정성태4/14/20219482.NET Framework: 1037. openssl의 PEM 개인키 파일을 .NET RSACryptoServiceProvider에서 사용하는 방법 (2)파일 다운로드1
12597정성태4/13/20219496개발 환경 구성: 569. csproj의 내용을 공통 설정할 수 있는 Directory.Build.targets / Directory.Build.props 파일
12596정성태4/12/20219291개발 환경 구성: 568. Windows의 80 포트 점유를 해제하는 방법
12595정성태4/12/20218695.NET Framework: 1036. SQL 서버 - varbinary 타입에 대한 문자열의 CAST, CONVERT 변환을 C# 코드로 구현
... 31  32  33  34  35  36  37  38  39  [40]  41  42  43  44  45  ...