<제네릭이란?>
보통 클래스나 인터페이스, 메서드를 사용할 때, 동일한 기능을 수행하지만, 입력하는 데이터 형식만 틀린 경우 매개변수를 일일히 넣어서 클래스나 인터페이스, 메서드를 만들지 않고, 제네릭 타입(Generic Type)을 사용하여 만들 수 있음.
<제네릭 특징>
1.코드 재사용성이 높습니다. 2.성능이 좋습니다.
namespace Generics;
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Hello, World!");
//new LinqGeneric().Strat();
//new Generic1_2().Start();
new Generic4_2().Start();
}
}
Generic1_1
using System;
/*
받는 변수 타입이 다르지만 같은 기능을 하는 메서드 여러개를 만든 것
제네릭 사용 x
*/
class Generic1_1
{
static void ArrayIntPrint(int[] Num)
{
for (int Temp = 0; Temp < Num.Length; ++Temp)
{
Console.Write("{0} ", Num[Temp]);
}
Console.WriteLine();
//foreach (int Temp in Num)
//{
// Console.WriteLine("{0}", Temp);
//}
}
static void ArraydoublePrint(double[] Num)
{
for (int Temp = 0; Temp < Num.Length; ++Temp)
{
Console.Write("{0} ", Num[Temp]);
}
Console.WriteLine();
}
static void ArrayStringPrint(String[] Num)
{
for (int Temp = 0; Temp < Num.Length; ++Temp)
{
Console.Write("{0} ", Num[Temp]);
}
Console.WriteLine();
}
void Start()
{
int[] Numbers1 = {1,3,5,7,9 };
ArrayIntPrint(Numbers1);
Console.WriteLine();
double[] Numbers2 = { 1.1, 3.1, 5.1, 7.1, 9.1 }; // float형은 f를 붙여야하고, 그냥 실수면 double형이다.
ArraydoublePrint(Numbers2);
Console.WriteLine();
string[] Numbers3 = { "일","이","삼","사"}; // 문자열은 ""를 붙여줘야한다.
ArrayStringPrint(Numbers3);
Console.WriteLine();
}
}
Generic1_2
using System;
/*
Generic1.cs에서 변수 타입을 제네릭을 사용하여 메소드 1개로 합친 형태
*/
class Generic1_2
{
static void ArrayPrint<T>(T[] Num){
for(int i=0; i<Num.Length; ++i)
{
Console.Write("{0} ", Num[i]);
}
Console.WriteLine();
}
public void Start()
{
int[] Numbers1 = { 1, 3, 5, 7, 9 };
double[] Numbers2 = { 1.1, 3.1, 5.1, 7.1, 9.1 }; // float형은 f를 붙여야하고, 그냥 실수면 double형이다.
string[] Numbers3 = { "일", "이", "삼", "사" }; // 문자열은 ""를 붙여줘야한다.
ArrayPrint<int>(Numbers1); //제네릭 형태 메소드 부르기
ArrayPrint<double>(Numbers2); //메소드명<변수타입>(ABC); 변수타입과 넘길 변수의 형식을 맞춰 작성해줌
ArrayPrint<string>(Numbers3);
}
}
Generic2_1
using System;
/*
인자가 2개인 일반 메서드
int 배열을 2개씩 선언해줘서 int1에 있는 값을 int2에 대입하는 예제
*/
class Generic2_1
{
static void ArrayPrint<T>(T[] Num)
{
for (int Temp = 0; Temp < Num.Length; ++Temp)
{
Console.Write("{0} ", Num[Temp]);
}
Console.WriteLine();
}
static void ArrayIntCopy(int[] Dst, int[] Src)
{
for(int Temp = 0; Temp < Dst.Length; ++Temp)
{
Dst[Temp] = Src[Temp];
}
}
static void ArraydoubleCopy(double[] Dst, double[] Src)
{
for (int Temp = 0; Temp < Dst.Length; ++Temp)
{
Dst[Temp] = Src[Temp];
}
}
public void Start()
{
int[] ArrayInt1 = {2, 4, 6, 8}; // 힙영역에 만들어서 값을 넣은거임
int[] ArrayInt2 = new int[4]; // 힙영역에 만들기만했고, 0으로 채워져있는거임.
ArrayIntCopy(ArrayInt2, ArrayInt1); //ArrayInt2 = ArrayInt1;와 같은 기능을 하게 만든 메서드
ArrayPrint<int>(ArrayInt2);
double[] Arraydouble1 = { 2.1, 4.1, 6.1, 8.1 };
double[] Arraydouble2 = new double[4];
ArraydoubleCopy(Arraydouble2, Arraydouble1);
ArrayPrint<double>(Arraydouble2);
}
}
Generic2_2
using System;
/*
인자가 2개인 제네릭
int 배열을 2개씩 선언해줘서 int1에 있는 값을 int2에 대입하는 예제
Generic2_1.cs 코드의 중복 부분을 제네릭을 사용하여 합침
*/
class Generic2_2
{
static void ArrayPrint<T>(T[] Num)
{
for (int Temp = 0; Temp < Num.Length; ++Temp)
{
Console.Write("{0} ", Num[Temp]);
}
Console.WriteLine();
}
static void ArrayCopy<T>(T[] Dst, T[] Src)
{
for(int Temp = 0; Temp < Dst.Length; ++Temp)
{
Dst[Temp] = Src[Temp];
}
}
public void Start()
{
int[] ArrayInt1 = {2, 4, 6, 8}; // 힙영역에 만들어서 값을 넣은거임
int[] ArrayInt2 = new int[4]; // 힙영역에 만들기만했고, 0으로 채워져있는거임.
ArrayCopy(ArrayInt2, ArrayInt1); //ArrayInt2 = ArrayInt1;와 같은 기능을 하게 만든 메서드
ArrayPrint<int>(ArrayInt2);
double[] Arraydouble1 = { 2.1, 4.1, 6.1, 8.1 };
double[] Arraydouble2 = new double[4];
ArrayCopy(Arraydouble2, Arraydouble1);
ArrayPrint<double>(Arraydouble2);
}
}
Generic3
using System;
/*
제네릭 : 넘기는 다른 타입의 인자값 2개를 한번에 선언
*/
class Program
{
static void TwoPrint<T1, T2>(T1 Arg1, T2 Arg2)
{
Console.WriteLine(Arg1);
Console.WriteLine(Arg2);
}
public void Start()
{
TwoPrint(3, 2.1);
TwoPrint("헬로", 4.0f);
}
}
Generic4_1
using System;
/*
일반 클래스를 사용한 예제
*/
class Generic4_1
{
public void Start()
{
FACT obj = new FACT();
obj.Value = 100;
obj.Print();
}
}
public class FACT
{
public int Value;
public void Print()
{
Console.WriteLine("FACT Value = {0}", Value);
}
}
Generic4_2
using System;
/*
Generic4_1.cs 코드를 제네릭 클래스 사용 형태로 변경한 예제
*/
class Generic4_2
{
public void Start()
{
// 제레릭 클래스를 호출할때는 아래와 같이 선언
FACT<int> obj = new FACT<int>(); //클래스명<type> abc = new 클래스명<type>();
obj.Value = 100;
obj.Print();
}
}
public class FACT<T> // class이름 뒤에 '<T>'를 붙여 제네릭 클래스를 나타냄
{
public int Value; //인자가 1개일때는 변수 타입을 바로 지정하여 public int Value;형태로 작성하여도 T가 바로 value로 들어감
//인자가 2개 이상일때는 public T1 Value; 형태로 작성해야 함
public void Print()
{
Console.WriteLine("FACT Value = {0}", Value);
}
}
Generic5
using System;
/*
제네릭 클래스 인자 2개 선언
*/
class Generic5
{
public void Start()
{
FACT<int,string> obj = new FACT<int, string>();
obj.Value1 = 100;
obj.Value2 = "제네릭 인자2개 연습";
obj.Print();
}
}
public class FACT<T1,T2> // class이름 뒤에 '<T>'를 붙여 제네릭 클래스를 나타냄
{
//받은 제네릭 인자 타입을 변수로 선언
public T1 Value1;
public T2 Value2;
public void Print()
{
Console.WriteLine("FACT Value = {0}, {1}", Value1, Value2);
}
}
LinqGeneric
using System;
using System.Collections.Generic;
using System.Linq;
public class LinqGeneric
{
/*
Linq 제네릭 : 클래스형
*/
public class Customer{
public string LastName { get; set; }
public string FirstName { get; set; }
public string City { get; set; }
public List<int> Scores;
}
public static List<Customer> customers = new List<Customer>
{
new Customer {LastName="Kim", FirstName="AA", City="London"},
new Customer {LastName="Sun", FirstName="BB", City="London"},
new Customer {LastName="Park", FirstName="CC", City="Seoul"},
new Customer {LastName="Yee", FirstName="DD", City="Pusan"}
};
public void Strat(){
IEnumerable<Customer> customerQuery =
from cust in customers
where cust.City == "London"
select cust;
foreach (Customer customer in customerQuery)
{
Console.WriteLine(customer.LastName + ", " + customer.FirstName);
}
var customerQuery2 =
from cust in customers
where cust.City == "London"
select cust;
foreach(var customer in customerQuery2)
{
Console.WriteLine(customer.LastName + ", " + customer.FirstName);
}
}
}
GenericExample
/*
<제네릭이란?>
보통 클래스나 인터페이스, 메서드를 사용할 때, 동일한 기능을 수행하지만, 입력하는 데이터 형식만 틀린 경우
매개변수를 일일히 넣어서 클래스나 인터페이스, 메서드를 만들지 않고, 제네릭 타입(Generic Type)을 사용하여 만들 수 있음.
<제네릭 특징>
1.코드 재사용성이 높습니다.
2.성능이 좋습니다.
*/
//동일 기능을 하지만 변수 타입만 다른 메서드 2개를 제네릭을 사용하여 1개의 메서드로 변경할 수 있음
class GenericExample{
//동일 한 기능을 하는 메서드 Swap1, Swap2
void Swap1(ref int x, ref int y)
{
var temp = y;
y = x;
x = temp;
}
void Swap2(ref float x, ref float y)
{
var temp = y;
y = x;
x = temp;
}
// Swap1,2를 Swap3<T> 제네릭 메서드로 합쳐서 만들 수 있음
void Swap3<T>(ref T x, ref T y)
{
var temp = y;
y = x;
x = temp;
}
}
/*
<제내릭 사용방법>
멤버변수의 타입을 미리 결정하지 않고, 사용할 때 제넥릭 클래스를 사용합니다.
public class 클래스-이름<T>
{ public T ... }
*/
public class GenericsClassExample
{
public class GenericsClass<T>
{
private T m_Value;
public GenericsClass(T value)
{
m_Value = value;
}
public void Method1()
{
Console.WriteLine(m_Value);
}
}
void Start()
{
GenericsClass<int> m_GenericsClass1 = new GenericsClass<int>(5);
m_GenericsClass1.Method1(); // 출력 : 5
GenericsClass<float> m_GenericsClass2 = new GenericsClass<float>(5.1f);
m_GenericsClass2.Method1(); // 출력 : 5.1
}
}
/*
<제네릭 클래스>
C#는 기본적으로 제공하는 제네릭 클래스가 많습니다.
대표적인 것이 List<T>, Dictionary<T>, LinkedList<T> 등이 있습니다.
<제네릭 타입 제약 (Type Constraint)>
제네릭 타입을 선언할 때 제약 조건을 걸 수 있습니다.
사용방법은 제네릭 선언 후 where T : 제약조건 과 같은 식으로 where 뒤에 제약 조건을 붙이면 됩니다.
<타입제약>
new 제약 조건1 : class Class1<T> where T : new() // T는 디폴트 생성자를 가져야 함
new 타입 제약2 : class Class1<T> where T : IComparable, new() // T는 디폴트 생성자를 가져야 함
<형식에 대한 조건>
class Class0<T> where T : new() // T는 매개 변수가 없는 public 생성자가 있어야 합니다
class Class1<T> where T : struct // T는 Value 타입임 : null을 허용하지 않는 값 형식
class Class2<T> where T : class // T는 참조 타입임
class Class4<T> where T : Class3 // T는 Class3의 파생클래스어야 함
class Class4<T> where T : IComparable // T는 IComparable 인터페이스를 가져야 함.
class Class5<T, U> // 복수 타입의 매개변수 제약
where T : class
where U : struct
{ }
<제네릭 메서드>
T 메소드-이름<T>(T arg)
{
T temp = arg;
//...
return temp;
}
*/
public class GenericExample2
{
void Start()
{
int iX = 1, iY = 2;
Swap(ref iX, ref iY);
Console.WriteLine($"x = {iX} y = {iY}"); // 출력 : x = 2 y = 1
string sX = "ab", sY = "cd";
Swap(ref sX, ref sY);
Console.WriteLine($"x = {sX} y = {sY}"); // 출력 : x = cd y = ab
}
void Swap<T>(ref T x, ref T y)
{
var temp = y;
y = x;
x = temp;
}
}
//ref를 사용하면 Add(ref abc); 형태만 작성하더라도 abc의 변수값이 Add 함수 내에서 변경된 값으로 저장됨.
/*
<제내릭 배열>
*/
class GenericExample3
{
void Start()
{
int[] arr = { 0, 1, 2, 3, 4 };
List<int> list = new List<int>();
for (int x = 5; x < 10; x++)
{
list.Add(x);
}
ProcessItems<int>(arr);
ProcessItems<int>(list);
}
static void ProcessItems<T>(IList<T> coll)
{
// IsReadOnly는 배열에 대해 True를 반환하고 목록에 대해 False를 반환합니다.
//coll.IsReadOnly : ICollection<T>가 읽기 전용인지 여부를 나타내는 값을 가져옵니다.
Console.WriteLine("IsReadOnly returns {0} for this collection.", coll.IsReadOnly);
// 다음 명령문은 배열에 대해 런타임 예외를 발생시키지만 목록에 대해서는 그렇지 않습니다.
//coll.RemoveAt(4);
foreach (T item in coll)
{
System.Console.Write(item.ToString() + " ");
}
System.Console.WriteLine();
}
}
/*
<제네릭 대리자>
delegate EventHandler
*/
class GenericExample4
{
delegate void StackEventHandler<T, U>(T sender, U eventArgs);
class Stack<T>
{
public class StackEventArgs : System.EventArgs { }
public event StackEventHandler<Stack<T>, StackEventArgs> StackEvent;
protected virtual void OnStackChanged(StackEventArgs a)
{
StackEvent(this, a);
}
}
class SampleClass
{
public void HandleStackChange<T>(Stack<T> stack, Stack<T>.StackEventArgs args) { }
}
public static void Test()
{
Stack<double> s = new Stack<double>();
SampleClass o = new SampleClass();
s.StackEvent += o.HandleStackChange;
}
}
'C#' 카테고리의 다른 글
C# 문법 공부 : HttpWebRe (0) | 2024.04.29 |
---|---|
C# 문법 공부 : Gif Image (0) | 2024.04.29 |
C# 문법 공부 : Files (0) | 2024.04.29 |
C# 문법 공부 : DelegateSample (0) | 2024.04.29 |
C# 문법 공부 : Network (0) | 2024.04.29 |