[C# 교과서] 48~49. 제네릭 클래스 만들기, 확장 메서드 만들기
사용자 정의 클래스를 매개변수로 사용하는 제네릭 클래스
- 성능 향상을 가져다 주는 기법을 제네릭
- 매개변수화된 형식을 만드는 데 사용
- 제네릭에 전달하는 매개변수를 형식 매개변수라고 함
컬렉션 이니셜라이저로 제네릭 리스트 초기화
using System;
class Person
{
public string Name { get; set; }
}
class Main2
{
static void Main()
{
List<Person> people = new List<Person>
{
new Person { Name = "이재현"},
new Person { Name = "이주연"},
new Person { Name = "김영훈"}
};
foreach (var person in people)
{
Console.WriteLine(person.Name);
}
}
}
List<T> 형태의 컬렉션 개체를 선언과 동시에 특정 개체 값으로 초기화 가능
이러한 내용을 컬렉션 이니셜라이저라고 함
제네릭 클래스에 사용자 정의 클래스 사용하기
List<T> 형태의 T에 사용자 정의 클래스를 매개 변수로 사용 가능
이또한 컬렉션 개체를 생성할 때 바로 특정 요소로 초기화 가능
using System;
class Person
{
public int Age { get; set; }
public string Name { get; set; }
}
class Main2
{
static void Main()
{
var people = new List<Person>()
{
new Person() { Age = 27, Name = "이재현"},
new Person() { Age = 26, Name = "이주연"},
new Person() { Age = 27, Name = "김영훈"}
};
foreach (var person in people)
{
Console.WriteLine($"{person.Name}은 {person.Age}살 입니다.");
}
}
}
제네릭 개체를 초기화하는 세 가지 방법 정리
1. List<T>에 List<Insolation> 형태로 사용자 정의 클래스를 넣고 개체 생성, 리스트를 선언과 동시에 초기화할 때 컬렉션 이니셜라이저를 사용하여 한 번에 데이터 여러개를 줄 수 있음
2. 이미 기본 값으로 초기화딘 리스트에 추가로 데이터를 입력할 때는 Add()메서드에 개체를 개체 이니셜라이저로 줄 수 있음
3. AddRange() 메서드로 데이터 리스트 여러 개를 한꺼번에 줄 수 있음
* 리스트 값을 출력할 때는 foreach문으로 반복해서 사용
List<T>의 T에 사용자 지정 클래스 설정
using System;
class AreaCode
{
public string Number { get; set; }
public string Areaname { get; set; }
}
class Main2
{
static void Main()
{
List<AreaCode> areas = new List<AreaCode>();
AreaCode seoul = new AreaCode(); // 속성으로 개체 초기화
seoul.Number = "02";
seoul.Areaname = "서울";
AreaCode sejong = new AreaCode() // 개체 이니셜라이저로 개체 초기화
{
Number = "044",
Areaname = "세종"
};
areas.Add(seoul);
areas.Add(sejong);
foreach (var area in areas)
{
Console.WriteLine($"번호 : {area.Number}, 지역 : {area.Areaname}");
}
}
}
세미콜론 주의하기!!
사전 제네릭 클래스 소개
리스트, List<T> | 사전, Dictionary<Tkey, TValue> |
요소 하나에 값을 저장 | 요소 하나에 키와 값을 저장 |
인덱스를 사용하여 요소에 접근 | 키를 사용하여 요소에 접근 |
요소 값 중복을 허용 | 요소 중복은 허용하나 키 중복은 허용하지 않음 |
반복이 빠름 | 특정 키에 검색이 빠름 |
- Dictionary<Tkey, TValue> : 일반적인 형태로 저장하고, 정렬되지는 않음
- SortedList<Tkey, TValue> : 키로 정렬하고, 정렬된 데이터를 빠르게 출력
- SortedDictionary<Tkey, TValue> : 키로 정렬하고, 정렬되지 않은 데이터를 빠르게 출력
제네릭 인터페이스
- ICollection<T> 인터페이스 : 제네릭 컬렉션 관련 클래스의 부모 역할을 하는 인터페이스 중 하나인 ICollection<T> 인터페이스는 제네릭 컬렉션을 조작하는 메서드 정의를 제공
- Count : 요소 개수를 반환
- Add(T) : T 개체를추가
- Clear() : 항목을 모두 제거
- Contains(T) : 특정 값이 들어 있는지 여부를 확인
- Remove(T) : 맨 처음 발견되는 특정 개체를 제거
- IEnumberable<T> 인터페이스 : 컬렉션의 데이터를 읽기 전용으로 출력할 때 사용. 당연하게 데이터를 수정할 때는 사용 불가능.
문자열 배열을 사용하는 세 가지 방법
using System;
class Main2
{
static void Main()
{
// 문자열 배열을 선언하는 기본적인 방법
var a1 = new string[] { "Red", "Green", "Blue" };
// List<T> 개체를 생성한 후 문자열 배열을 ToList() 메서드로 변환
var a2 = new List<string>();
a2 = a1.ToList();
// IEnumerable<T> 개체를 생성한 후 문자열 배열을 바로 대입 가능
IEnumerable<string> a3 = a1;
// IEnumerable<T> 개체를 ToList() 메서드로 List<T> 형태로 변환
var a4 = a3.ToList();
// IEnumerable<T> 개체는 주로 foreach 문으로 반복 사용
foreach (var arr in a3)
{
Console.WriteLine(arr);
}
// string[], List<T> 개체는 for문으로 반복 가능
for (int i = 0; i < 3; i++)
{
Console.WriteLine($"{a1[i]},{a2[i]},{a4[i]}");
}
}
}
제네릭 클래스 만들기
using System;
// 클래스<T> 형태로 제네릭 클래스 만들기
public class Cup<T>
{
public T Content {get; set;}
}
class Main2
{
static void Main()
{
// T에 string을 전달하여 문자열을 저장하는 속성 생성
Cup<string> text = new Cup<string>();
text.Content = "문자열";
// T에 int를 전달하여 정수형을 저장하는 속성 생성
Cup<int> number = new Cup<int>();
number.Content = 1234;
Console.WriteLine($"{text.Content}, {number.Content}");
}
}
제네릭에 사용자 정의 형식 클래스 전달하기
using System;
class Juice {}
class Coffee {}
// 클래스<T> 형태로 제네릭 클래스 만들기
public class Cup<T>
{
public T Type {get; set;}
}
class Main2
{
static void Main()
{
Cup<Juice> juice = new Cup<Juice>();
juice.Type = new Juice();
Console.WriteLine(juice.Type.ToString());
var coffee = new Cup<Coffee> { Type = new Coffee() };
Console.WriteLine(coffee.Type.ToString());
}
}
출력결과 >
Juice
Coffee
설명 >
T 형식 매개변수로 전송하면 juice 개체의 Type 속성은 Juice 클래스의 인스턴스가 됨
제네릭 클래스와 제네릭 메서드
형식 매개변수 T는 클래스에 사용할 수 있으며 마찬가지로 필드, 속성, 메서드의 매개변수 형식 또는 반환형식에 사용할 수 있음
확장 메서드
- 이미 만들어 있는 클래스 기능 확장
- 클래스와 구조체, 인터페이스에서 사용 가능
- 특히 봉인(sealed) 클래스는 상속이 불가능하므로 봉인 클래스에 새로운 메서드를 적용하기가 유용
- static 키워드가 붙은 클래스에 static 메서드로 만들어짐
- 반드시 동일한 네임스페이스를 참조해야 함
- 확장 메서드의 첫 번째 매개변수에 this 키워드를 지정하여 확장 메서드를 사용할 개체 형식 선택
- 확장 메서드를 사용하면 같은 네임스페이스의 모든 클래스에서 해당 확장 메서드 호출 가능, 확장 메서드는 이미 완성된 기존 형식에 새로운 메서드를 추가하는 방법으로 사용
> public static void MethodName(this object obj, int i) { }
> public static void MethodName(this string str, int i) { }
확장 메서드 만들기 정리
- 정적 클래스에 정적 메서드로 구현
- 첫 번째 메서드 매개변수에 this 키워드를 붙임
- 같은 범위(scope)를 같은 네임스페이스에서 호출 가능
- 확장 메서드도 오버로드가 가능
확장 메서드로 문자열 기능 확장하기
using System;
static class ExtensionFunction
{
static string Three(this string value)
{
return value.Substring(0,3);
}
static void Main()
{
Console.WriteLine("안녕하세요".Three());
}
}
확장 메서드로 기존 형식에 새로운 메서드 추가하기
using System;
namespace ExtensionMethodDemo
{
public static class MyClass
{
public static int WordCount(this String str)
{
return str.Split(new char[] {' ','.','?'},
StringSplitOptions.RemoveEmptyEntries).Length;
}
}
class ExtensionMethodDemo
{
static void Main()
{
string s = "안녕하세요? 확장 메서드... ...";
Console.WriteLine(s.Length); // 문자 개수
Console.WriteLine(s.WordCount()); // 단어 개수
}
}
}
출력결과 >
20
3
문자열 변수 s에는 원래 WordCount() 라는 메서드가 없지만, 같은 네임스페이스에 정의된 MyClass의 WordCount() 확장 메서드를 s 변수에서 사용할 수 있게 함
확장 메서드를 사용하여 형식에 메서드 추가
다음 코드의 Original 클래스는 아무 멤버도 갖지 않은 클래스이지만, OriginalExtension 클래스에서 NewMethod()를 참조해서 새로운 메서드를 추가하여 사용할 수 있음
using System;
public class Original { }
public static class OriginalExtension
{
public static void NewMethod(this Original original)
=> Console.WriteLine("새로운 메서드 추가");
}
class ExtensionMethodNote
{
static void Main()
{
(new Original()).NewMethod(); // 확장 메서드 호출
}
}