C# 7 : ref local
C# 7 에서는 로컬변수에 대해 ref 를 적용하여 동일한 메모리 공간에 대한 레퍼런스를 얻을 수 있는데, 이를 ref local 이라 부른다. 아래 예제에서, 로컬변수 a에 대해 ref 를 적용하여 변수 b에 a의 레퍼런스를 할당하였다. 다음 b 의 값을 2로 변경하였는데, a 나 b가 동일한 메모리를 가리키고 있으므로 a, b 값이 모두 2가 된다.
예제
int a = 1;
// ref local
ref int b = ref a;
b = 2;
WriteLine($"{a}, {b}"); // "2, 2" 출력
// ref local
ref int b = ref a;
b = 2;
WriteLine($"{a}, {b}"); // "2, 2" 출력
C# 7 : ref return
C# 7 이전 버젼에서는 메서드의 입력 파라미터에 대해서는 ref를 적용할 수 있었지만 리턴 타입에 대해서는 ref 를 적용할 수 없었다. C# 7 에서는 이제 리턴 타입에 대해서도 ref 타입을 허용하게 되었다.
예를 들어, 아래 GameData 클래스의 GetScore() 메서드는 리턴 타입이 "ref int"로 되어 있고 실제 return 문에서도 "ref scores[id]" 처럼 ref 값을 리턴하고 있는데, 이것을 ref return 이라 부른다. 이러한 ref return 기능은 큰 용량의 데이타에서 특정 요소를 리턴하여 읽거나 변경할 때 유용하게 사용될 수 있다.
한가지 주의할 점은 ref return을 하는 메서드를 호출할 때는 아래 예제의 "ref _gameData.GetScore(10)" 에서와 같이 메서드 호출문 앞에 ref를 붙여야 한다.
예를 들어, 아래 GameData 클래스의 GetScore() 메서드는 리턴 타입이 "ref int"로 되어 있고 실제 return 문에서도 "ref scores[id]" 처럼 ref 값을 리턴하고 있는데, 이것을 ref return 이라 부른다. 이러한 ref return 기능은 큰 용량의 데이타에서 특정 요소를 리턴하여 읽거나 변경할 때 유용하게 사용될 수 있다.
한가지 주의할 점은 ref return을 하는 메서드를 호출할 때는 아래 예제의 "ref _gameData.GetScore(10)" 에서와 같이 메서드 호출문 앞에 ref를 붙여야 한다.
예제
using static System.Console;
namespace ConsoleApp1
{
class Program
{
static GameData _gameData = new GameData();
static void Main(string[] args)
{
// 10번째 배열요소에 대한 ref
ref int score10 = ref _gameData.GetScore(10);
// 10번째 배열요소에 쓰기
score10 = 99;
WriteLine(_gameData.GetScore(10)); // 99
}
}
class GameData
{
private int[] scores = new int[100];
// ref return
public ref int GetScore(int id)
{
//...
return ref scores[id];
}
}
}
namespace ConsoleApp1
{
class Program
{
static GameData _gameData = new GameData();
static void Main(string[] args)
{
// 10번째 배열요소에 대한 ref
ref int score10 = ref _gameData.GetScore(10);
// 10번째 배열요소에 쓰기
score10 = 99;
WriteLine(_gameData.GetScore(10)); // 99
}
}
class GameData
{
private int[] scores = new int[100];
// ref return
public ref int GetScore(int id)
{
//...
return ref scores[id];
}
}
}
C# 7 : ref struct
C# 7.2에서 ref struct를 도입하였는데, 이는 구조체(struct)가 항상 스택(stack) 상에 존재하도록 여러가지 제약점을 가한 구조체이다. ref struct가 아닌 지금까지의 구조체(non-ref struct)는 일반적으로 스택에 존재하지만, Boxing을 하거나 다른 클래스의 멤버로 사용되는 경우 Heap 상에 존재할 수 있었다. ref struct는 이렇게 Heap 상에 존재할 수 있는 것음 금지시키고 항상 스택 상에 존재하도록 한 것으로 이렇게 하기 위해서 ref struct는 다른 클래스의 멤버가 될 수 없고, object boxing 등이 금지되는 등등의 여러가지 제약점을 갖게 된다. 컴파일러는 ref struct 타입이 Heap 상에 존재할 수 있는 상황들을 자동 체크해서 만약 그런 경우 컴파일 에러를 발생한다.
일반적으로 ref struct는
(1) 스택 상에서 사용함으로써 성능향상을 꾀하거나 (즉, Heap을 사용하지 않음으로 해서 가비지컬렉션(GC)의 부하를 줄임)
(2) 다른 ref struct 타입을 그 구조체의 멤버로 포함할 경우에 사용한다 (ref struct 멤버를 포함하는 구조체가 Heap 상에 할당되어서는 않되도록)
C# 7.2에서 ref struct를 도입한 것은 특히 Span<T> 구조체를 구현하는데 필요하였는데, Span<T>는 ref struct 타입으로 stack memory, managed memory, unmanaged memory 등의 임의의 메모리 공간에 존재하는 연속적인 공간으로 표현하는 Value 타입이다.
아래 예제는 ref struct 를 정의한 것으로, 필드 A와 B는 Span<T> ref struct 타입으므로 구조체 MyStruct를 'ref struct'로 정의하였다. 만약 이것은 그냥 'public struct MyStruct'으로 정의하면, 컴파일러 에러 CS8345 (Field or auto-implemented property cannot be of type 'Span<T>' unless it is an instance member of a ref struct) 가 발생한다.
일반적으로 ref struct는
(1) 스택 상에서 사용함으로써 성능향상을 꾀하거나 (즉, Heap을 사용하지 않음으로 해서 가비지컬렉션(GC)의 부하를 줄임)
(2) 다른 ref struct 타입을 그 구조체의 멤버로 포함할 경우에 사용한다 (ref struct 멤버를 포함하는 구조체가 Heap 상에 할당되어서는 않되도록)
C# 7.2에서 ref struct를 도입한 것은 특히 Span<T> 구조체를 구현하는데 필요하였는데, Span<T>는 ref struct 타입으로 stack memory, managed memory, unmanaged memory 등의 임의의 메모리 공간에 존재하는 연속적인 공간으로 표현하는 Value 타입이다.
아래 예제는 ref struct 를 정의한 것으로, 필드 A와 B는 Span<T> ref struct 타입으므로 구조체 MyStruct를 'ref struct'로 정의하였다. 만약 이것은 그냥 'public struct MyStruct'으로 정의하면, 컴파일러 에러 CS8345 (Field or auto-implemented property cannot be of type 'Span<T>' unless it is an instance member of a ref struct) 가 발생한다.
예제
// Span<int>는 ref struct 타입의 멤버이므로
// ref struct 로 정의함
public ref struct MyStruct
{
public bool IsValid;
public Span<int> A; // ref struct 멤버
public Span<int> B; // ref struct 멤버
}
// ref struct 로 정의함
public ref struct MyStruct
{
public bool IsValid;
public Span<int> A; // ref struct 멤버
public Span<int> B; // ref struct 멤버
}