C# Tuple, ValueTuple
1. Tuple
private Tuple<EErrorCode, CQuest, CReward> CompleteQuest(Int32 iID_)
{
return Tuple.Create<EErrorCode, CQuest, CReward>(EErrorCode.INVALID_ID, null, null);
}
public void Main()
{
var tuResult = CompleteQuest(5);
if(tuResult.Item1 != EErrorCode.OK)
{
Logging($"Error : {tuResult.Item1}.");
return;
}
UpdateQuest(tuResult.Item2);
ReceiveReward(tuResult.Item3);
}
Tuple은 C++의 그것과 동일하다.
여러 가지를 한 번에 전달해야 하는데 구조체 같은 것으로 감쌀 만큼 재사용성이나 스펙이 크지 않을 때 사용한다.
2. Tuple 단점
// Rank Score 각각 보상을 받았는지?
private Tuple<bool, bool> IsReceivable()
{
bool bRankReward = false;
bool bScoreReward = false;
return Tuple.Create(bRankReward, bScoreReward);
}
private Tuple<Int64, Int32, Int64> GetRankData(Int64 biUID_)
{
Int32 iRank = RedisManager.GetRank(biUID_);
Int64 biScore = RedisManager.GetScore(biUID_);
return Tuple.Create<Int64, Int32, Int64>(biUID_, iRank, biScore);
}
public void ReceivePVPReward()
{
var tuReceivable = IsReceivable();
if(tuReceivable.Item1 & tuReceivable.Item2 == false)
{
return;
}
var tuRankData = GetRankData(biMyUID);
// Rank 보상을 받는다.
if (tuReceivable.Item1 == true)
{
ReceiveRankReward(tuRankData.Item2);
}
// Score 보상을 받는다.
if (tuReceivable.Item2 == true)
{
ReceiveScoreReward(tuRankData.Item3);
}
}
같은 타입을 리턴했을때 구분하는 게 거의 불가능하고, // Tuple<bool, bool> 쓰면 지옥 시작.
Tuple을 받은 측에서는 코드를 파악할 때 난감한 문제가 있다. // Item1 는 뭐고 Item 2는 뭐지,,?
ValueTuple를 쓰면 위 문제가 꽤나 만족스럽게 해결된다.
3. ValueTuple
// Rank Score 각각 보상을 받았는지?
private (bool bRankReward, bool bScoreReward) IsReceivable()
{
bool bRankReward = false;
bool bScoreReward = false;
return (bRankReward, bScoreReward);
}
private (Int64 biUID, Int32 iRank, Int64 biScore) GetRankData(Int64 biUID_)
{
Int32 iRank = RedisManager.GetRank(biUID_);
Int64 biScore = RedisManager.GetScore(biUID_);
return (biUID_, iRank, biScore);
}
public void ReceivePVPReward()
{
var tuReceivable = IsReceivable();
if(tuReceivable.bRankReward & tuReceivable.bScoreReward == false)
{
return;
}
var tuRankData = GetRankData(biMyUID);
// Rank 보상을 받는다.
if (tuReceivable.bRankReward == true)
{
ReceiveRankReward(tuRankData.iRank);
}
// Score 보상을 받는다.
if (tuReceivable.bScoreReward == true)
{
ReceiveScoreReward(tuRankData.biScore);
}
}
Tuple<bool, bool>. -> (bool bName1, bool bName2)
tuResult.item1 -> tuResult.bName1
ValueTuple로 바꾸면 가독성이 훨씬 깔끔해진다.
추가적으로 ValueTuple은 Tuple과 달리 안의 값을 수정이 가능하고 값 타입으로 저장하며,
Null을 넣을 수가 없어서 실패 체크를 따로 Flag 같은 것으로 해줘야 한다. (Default 키워드로 어느정도 때울 수는 있는데 비추.)
ValueTuple은 C# 7.0 (.Net 4.7 이상)에서 정식 지원하고, 하위 버전에서는 NuGet으로 받아서 사용이 가능하다.
(ValueTuple Default는 C# 7.3부터 가능함.)
더 깊게
Tuple은 값 타입, ValueTuple은 참조 타입이다.
TupleType은 ValueTuple은 것을 지칭한다고 볼 수 있으며, (int, int)와 같이 선언하면 내부적으로 ValueTuple<int,int>로 변환된다고 한다.