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

... 61  62  63  64  65  66  67  68  [69]  70  71  72  73  74  75  ...
NoWriterDateCnt.TitleFile(s)
11922정성태5/29/201911568.NET Framework: 840. ML.NET 데이터 정규화파일 다운로드1
11921정성태5/28/201916503Math: 55. C# - 다항식을 위한 최소 자승법(Least Squares Method)파일 다운로드1
11920정성태5/28/201910060.NET Framework: 839. C# - PLplot 색상 제어
11919정성태5/27/201913206Math: 54. C# - 최소 자승법의 1차 함수에 대한 매개변수를 단순 for 문으로 구하는 방법 [1]파일 다운로드1
11918정성태5/25/201914382Math: 53. C# - 행렬식을 이용한 최소 자승법(LSM: Least Square Method)파일 다운로드1
11917정성태5/24/201914527Math: 52. MathNet을 이용한 간단한 통계 정보 처리 - 분산/표준편차파일 다운로드1
11916정성태5/24/201912515Math: 51. MathNET + OxyPlot을 이용한 간단한 통계 정보 처리 - Histogram파일 다운로드1
11915정성태5/24/201914808Linux: 11. 리눅스의 환경 변수 관련 함수 정리 - putenv, setenv, unsetenv
11914정성태5/24/201914507Linux: 10. 윈도우의 GetTickCount와 리눅스의 clock_gettime파일 다운로드1
11913정성태5/23/201912142.NET Framework: 838. C# - 숫자형 타입의 bit(2진) 문자열, 16진수 문자열 구하는 방법파일 다운로드1
11912정성태5/23/201911791VS.NET IDE: 137. Visual Studio 2019 버전 16.1부터 리눅스 C/C++ 프로젝트에 추가된 WSL 지원
11911정성태5/23/201910869VS.NET IDE: 136. Visual Studio 2019 - 리눅스 C/C++ 프로젝트에 인텔리센스가 동작하지 않는 경우
11910정성태5/23/201919530Math: 50. C# - MathNet.Numerics의 Matrix(행렬) 연산 [1]파일 다운로드1
11909정성태5/22/201913952.NET Framework: 837. C# - PLplot 사용 예제 [1]파일 다운로드1
11908정성태5/22/201912338.NET Framework: 836. C# - Python range 함수 구현파일 다운로드1
11907정성태5/22/201910126오류 유형: 541. msbuild - MSB4024 The imported project file "...targets" could not be loaded
11906정성태5/21/201910083.NET Framework: 835. .NET Core/C# - 리눅스 syslog에 로그 남기는 방법
11905정성태5/21/201910756.NET Framework: 834. C# - 폴더 경로 문자열에서 "..", "." 표기를 고려한 최종 문자열을 얻는 방법 - 두 번째 이야기
11904정성태5/21/201917030.NET Framework: 833. C# - Open Hardware Monitor를 이용한 CPU 온도 정보 [1]파일 다운로드1
11903정성태5/21/201911982오류 유형: 540. .NET Core - System.PlatformNotSupportedException: The named version of this synchronization primitive is not supported on this platform.
11902정성태5/21/201911132오류 유형: 539. mstest 실행 시 "The directory name is invalid." 오류 발생
11901정성태5/21/201912281오류 유형: 538. msbuild 오류 - Could not find a part of the path '%LOCALAPPDATA%\Temp\2\.NETFramework,Version=v4.0.AssemblyAttributes.cs'
11900정성태5/18/201911553오류 유형: 537. "sfc /scannow" 실행 중 시스템이 부팅되는 현상
11899정성태5/17/201912584Linux: 9. Linux에서 윈도우의 OutputDebugString 대신 사용할 수 있는 syslog [1]
11898정성태5/16/201913968VC++: 130. C++ string의 c_str과 data 함수의 차이점 [3]
11897정성태5/16/201920638오류 유형: 536. Visual Studio - "Developer Pack"을 설치했는데도 "대상 프레임워크" 목록에 나오지 않는 경우 [2]
... 61  62  63  64  65  66  67  68  [69]  70  71  72  73  74  75  ...