sequence의 elements 중 하나 또는 범위에 어떻게 접근할 수 있는지 살펴보자.
Indices
sequence의 index를 나타낸다. C# 8.0부터 ^ operator로 index를 지정할 수 있다. constructor Index는 2개의 paramter로 구성되어 있다.
public Index (int value, bool fromEnd = false);
index는 0보다 크거나 같아야 하며, fromEnd는 optional이다.
Index는 아래와 같이 사용할 수 있다.
public class IndexExamples
{
public static string GetFirst(string[] names)
{
var index = new Index(0);
return names[index];
}
public static string GetLastMethod1(string[] names)
{
var index = new Index(1, true);
return names[index];
}
}
GetFirst는 array의 첫번째 element를 반환하고, GetLastMethod1은 array의 마지막 element를 반환한다.
코드는 정상 동작하지만, 우리는 더 심플하게 수정할 수 있다.
public static string GetLastMethod2(string[] names)
{
return names[^1];
}
public static string GetSecondLast(string[] names)
{
return names[^2];
}
GetLastMethod1, GetLastMethod2 모두 동일한 결과가 있지만, GetLastMethod2의 구분이 더 간결하다.
- names[^1] 은 names[names.Length - 1]과 동일하다.
Ranges
Ranges는 ..operator를 사용하여 sequence의 부분의 표현하는 간결한 방식이다. range operator는 slice의 시작과 끝을 명시한다. x..y를 예로 들어보자.
- x는 시작 index
- y는 마지막 index
- x, y는 선택적 값이다.
- range operator(..)는 시작 index(x)를 포함하고, 마지막 index(y)는 제외한다.
Range class의 constructor를 살펴보자.
public Range(Index start, Index end);
RangeExamples class를 만들고 range operator(..)를 다양하게 사용해보자.
public class RangeExamples
{
public static string[] GetAll(string[] arr)
{
return arr[..];
}
public static string[] GetFirstTwoElements(string[] arr)
{
var start = new Index(0);
var end = new Index(2);
var range = new Range(start, end);
return arr[range];
}
public static string[] GetFirstThreeElements(string[] arr)
{
return arr[..3];
}
public static string[] GetLastThreeElements(string[] arr)
{
return arr[^3..];
}
public static string[] GetThreeElementsFromMiddle(string[] arr)
{
return arr[3..6];
}
}
사용 제한
List<T>와 같은 내장타입이나 custom type과는 Range, Index를 사용할 수 없다. 하지만, 지원하도록 수정 가능하다. NameList class를 만들고, Index, Range를 지원하도록 수정해보자.
public class NameList : IEnumerable<string>
{
private List<string> _names;
public NameList()
{
_names = new List<string>();
}
public IEnumerator<string> GetEnumerator()
{
return _names.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return ((IEnumerable)_names).GetEnumerator();
}
public void Add(string name) => _names.Add(name);
}
NameList class에 대해서 index를 사용할 수 없다. indexable하게 수정하려면, object를 countable하게 바꿔야 한다. Count와 Length property를 추가한다.
Range를 사용할 수 있도록 Slice method를 추가한다.
public class NameList : IEnumerable<string>
{
private List<string> _names;
public NameList()
{
_names = new List<string>();
}
public string this[int index] => _names[index];
public int Count => _names.Count;
public IEnumerator<string> GetEnumerator()
{
return _names.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return ((IEnumerable)_names).GetEnumerator();
}
public void Add(string name) => _names.Add(name);
public List<string> Slice(int start, int length)
=> _names
.Skip(start)
.Take(length)
.ToList();
}
이제 Index와 Range를 사용할 수 있다.
public class NameListExamples
{
public static List<string> GetAll(NameList names)
{
return names[..];
}
public static List<string> GetFirstTwoElements(NameList names)
{
var start = new Index(0);
var end = new Index(2);
var range = new Range(start, end);
return names[range];
}
public static List<string> GetFirstThreeElements(NameList names)
{
return names[..3];
}
public static List<string> GetLastThreeElements(NameList names)
{
return names[^3..];
}
public static List<string> GetThreeElementsFromMiddle(NameList names)
{
return names[3..6];
}
public static string GetLastElement(NameList names)
{
return names[^1];
}
}
NameList class처럼 range, indice는 필요조건을 충족하는 어떤 class와도 사용 가능하다.
결론
C#에서의 Index, Range를 살펴보았다. 그리고, custom type에서 Index, Range를 지원하도록 수정해보았다.
'C#' 카테고리의 다른 글
C#] Span<T>, ReadOnlySpan<T> 과 메모리 성능 (0) | 2024.04.05 |
---|---|
C#] Properties vs Fields 차이점 (0) | 2024.02.15 |
C#] Optional Parameters (0) | 2024.02.14 |
C#] IDisposal Interface 구현 방법 (0) | 2024.02.14 |
C#] Managed vs Unmanaged Code (0) | 2024.02.14 |
댓글