방프리

21.03.27 Chapter3. 제네릭 활용 (Item 24) 본문

C#/Effective C#

21.03.27 Chapter3. 제네릭 활용 (Item 24)

방프리 2021. 3. 27. 01:34

Item 24 : 베이스 클래스나 인터페이스에 대해서 제네릭을 특화하지 말라

 

제네릭 클래스나 제네릭 메서드를 작성할 때는 사용자가 가능한 한 안전하고 혼란스럽지 않게 작성해야야한다.

특히 오버로드된 메서드가 여러 개인 경우, 컴파일러가 어떤 메서드를 선택하는지 정확히 알아야한다.

다음 예제를 통해서 어떤 메서드를 우선으로 호출하는지 확인해볼 수 있다.

using static System.Console;

public class MyBase
{
}

public interface IMessageWriter
{
    void WriteMessage();
}

public class MyDerived : MyBase, IMessageWriter
{
    void IMessageWriter.WriteMessage() => 
    	WriteLine("Inside MyDerived.WriteMessage");
}

public class AnotherType : IMessageWriter
{
    public void WriteMessage() => 
    	WriteLine("Inside AnotherType.WriteMessage");
}

class Program
{
    static void WriteMessage(MyBase b)
    {
    	WriteLine("Inside WriteMessage(MyBase)");
    }
    
    static void WriteMessage<T>(T obj)
    {
    	Write("Inside WriteMessage<T>(T): ");
        WriteLine(obj.ToString());
    }
    
    static void WriteMessage(IMessageWriter obj)
    {
    	Write("Inside WriteMessage(IMessageWriter): ");
        obj.WriteMessage();
    }
    
    static void Main(string[] args)
    {
    	MyDerived d = new MyDerived();
        WriteLine("Calling Program.WriteMessage");
        WriteMessage(d);
        WriteLine();

        WriteLine("Calling through IMessageWriter interface");
        WriteMessage((IMessageWriter)d);
        WriteLine();

        WriteLine("Case to base object");
        WriteMessage((MyBase)d);
        WriteLine();

        WriteLine("Another Type test: ");
        AnotherType anObject = new AnotherType();
        WriteMessage(anObject);
        WriteLine();

        WriteLine("Cast to IMessageWriter:");
        WriteMessage((IMessageWriter)anObject);
    }
}

위의 코드는 다음의 결과를 나타낸다.

Calling Program.WriteMessage
Inside WriteMessage<T>(T): Item14.MyDerived

Calling through IMessageWriter interface Inside
WriteMessage(IMessageWriter): Inside MyDerived.WriteMessage

Cast to base object
Inside WriteMessage(MyBase)

Another Type test:
Inside WriteMessage<T>(T): Item14.AnotherType

Cast to IMessageWriter:
Inside WriteMessage(IMessageWriter): Inside AnotherType.WriteMessage

첫 번째 테스트는 MyBase를 상속한 MyDerived 클래스는 WriteMessage(MyBase b)보다 WriteMessage<T>(T obj)가

더 정확히 일치힌다. 타입 매개변수인 T를 MyDerived로 대체하면 컴파일러 입장에서는 정확히 일치하는 메서드를

찾을 수 있기 때문이다.

두 번째, 세 번째는 명시적 형변환이 메서드 확인 규칙에 어떤 영향을 미치는지 보여준다.

네 번째와 마지막 테스트는 특정 인터페이스를 구현하고 있는 타입을 사용할 경우 어떤 메서드가 선택되는지

확인할 수 있다.

제네릭 타입을 이용한 오버로드 메서드를 사용할 때 위의 네 가지 규칙을 정확히 인지 후 사용해야

실수를 방지할 수 있다.

Comments