프로그래밍 언어/C#

C# 제네릭 (C++ > 템플릿)

ShovelingLife 2022. 6. 13. 11:03
using System;
 
public class Test<T> where T : class
{
    public T Get_val(T _val)
    {
        return _val;
    }  
}
 
public class Another
{
    public static void Main()
    {
        Test<int> int_test = new Test<int>();
        Console.WriteLine(int_test.Get_val(10));
    }
}

첫째 클래스 선언 라인에 보면 where T : class라고 명시 되어있다, Main 함수 내에 선언하고 있는건 int형이다, 따라서 아래와 같이 에러가 뜬다.

C++와 가장 큰 차이점은 C#의 템플릿은 제약 사항이 많다는 것이다, SFINAE도 있겠지만 가장 큰 특징은 추상화를 할 수 없다는 점이다 따라서 아래는 에러가 뜬다.

using System;
 
public class Test<T> where T : class
{
    public static void Print(T _pos)
    {
        Console.WriteLine(String.Format("x값 : {0}, y값 : {1}"), _pos.x, _pos.y);
    }
}
 
public class Pos
{
    public int x, y;
 
    public Pos()
    {
 
    }
 
    public Pos(int x, int y)
    {
        this.x = x;
        this.y = y;
    }
}
 
public class Another
{
    public static void Main()
    {
        Test<Pos>.Print(new Pos(5, 7));
    }
}

살짝 바꿔보면 아래와 같이 정상적으로 출력되는 모습을 볼 수 있다.

using System;
 
public class Pos
{
    public int x, y;
 
    public Pos()
    {
 
    }
 
    public Pos(int x, int y)
    {
        this.x = x;
        this.y = y;
    }
}
 
public class Test<T> where T : Pos
{
    public static void Print(T _pos)
    {
        Console.WriteLine($"x값 :{_pos.x}, y값 : {_pos.y}");
    }
}
 
public class Another
{
    public static void Main()
    {
        Test<Pos>.Print(new Pos(5, 7));
    }
}

결과

근데 만약에 여러 클래스를 받고싶다면 어떻게 해야할까? where T : Pos 이 부분에서 Pos를 class로 다시 바꿔주고 리플렉션 기능을 사용해야 한다, System.Type 하지만 C#에선 룰이 존재한다, 바로 switch가 안된다는 부분이다.

public class Test<T> where T : class
{
    public static void Print(T _pos)
    {
        Type type = _pos.GetType();
 
        switch(type)
        {
            case typeof(int):
                break;
        }
    }
}

이렇게 선언할 시 아래와 같은 에러가 뜬다.

부모 클래스로부터 상속을 받아 오버라이딩한 후 Print함수에서 부모 클래스로 캐스팅을 하여 널이 아닐 시 출력하는 예제. Dog Cat 두 클래스 모두 다 Animal로부터 상속을 받기 때문에 as로 캐스팅 할 시 널이 될 수가 없다.

using System;
 
public class Animal
{
    string sound_type = "";
 
    public Animal(string _sound_type)
    {
        this.sound_type = _sound_type;
    }
 
    public virtual void Play_sound()
    {
        Console.WriteLine(sound_type);
    }
}
 
public class Dog : Animal
{
    public Dog(string _sound_type) : base(_sound_type)
    {
 
    }
 
    public override void Play_sound()
    {
        Console.WriteLine("이건 강아지 ");
        base.Play_sound();
    }
}
 
public class Cat : Animal
{
    public Cat(string _sound_type) : base(_sound_type)
    {
 
    }
 
    public override void Play_sound()
    {
        Console.WriteLine("이건 고양이 ");
        base.Play_sound();
    }
}
 
public class Test<T> where T : class
{
    public void Print(T _type)
    {
        Animal animal = _type as Animal;
 
        if (animal != null)
            animal.Play_sound();
    }
}
 
 
public class Another
{
    public static void Main()
    {
        Test<Animal> animal_test = new Test<Animal>();
        animal_test.Print(new Dog("멍멍"));
        animal_test.Print(new Cat("냐옹"));
    }
}

결과

마지막으로 템플릿 함수에 대해 알아보겠다. 

using System;

public class A
{
    public void Print()
    {
        Console.WriteLine("A 클래스");
    }
}

public class B
{
    public void Print<T>(T _t) where T : A
    {
        _t.Print();
    }
}

public class C
{
    public void Print<T>(T _t) where T : A
    {
        _t.Print();
    }

    public void Print()
    {
        Console.WriteLine("C 클래스");
    }
}

public class D<T> : A where T : class
{
    public void Test(T _t)
    {
        Type type = _t.GetType();

        if(type == typeof(C))
        {
            C c = _t as C;
            c.Print();
        }
    }
}

public class Test
{
    public static void Main()
    {
        B b = new B();
        A a = new A();
        C c = new C();
        D<C> d = new D<C>();
        b.Print<A>(a);
        c.Print<A>(a);
        d.Print();
        d.Test(c);
    }
}

결과

여기 메인 함수에서 에러를 뜨는 이유를 보자. D 클래스는 where절로 class라고 명시 되어있다, int형은 클래스가 아니므로 에러가 뜬다. c클래스 내에 Print 함수는 where절로 A클래스라고 명시 되어있다 따라서 에러가 뜬다, 추가적으로 덧붙이자면 Print 함수에서 꺽쇠 괄호 <클래스명> 클래스 명시는 생략해도 무방하다.