방프리

21.03.30 Chapter3. 제네릭 활용 (Item 26) 본문

C#/Effective C#

21.03.30 Chapter3. 제네릭 활용 (Item 26)

방프리 2021. 3. 30. 00:29

Item 26 : 제네릭 인터페이스와 논제네릭 인터페이스를 함께 구현하라

 

제네릭 타입은 C# 초기부터 있던 기능이 아니기 때문에 예전버전까지 호환해야한다면 이를 고려해야 한다.

세 가지 요소(클래스와 인터페이스, public 속성, Serialize)를 모두 지원해주어야 한다.

하단의 코드는 논제네릭 방식으로 구현된 (레거시 코드) Name 클래스이다.

public class Name : IComparable<Name>, IEquatable<Name>
{
    public string First { get; set; }
    public string Last { get; set; }
    public string Middle { get; set; }

    public int CompareTo(Name other)
    {
    	if (Object.ReferenceEquals(this, other))
        	return 0;
        if (Object.ReferenceEquals(other, null))
        	return 1;
        
        int rVal = Comparer<string>.Default.Compare(Last, other.Last);
        
        if (rVal != 0)
        	return rVal;
        
        rVal = Comparer<string>.Default.Compare(First, other.First);
        if (rVal != 0)
        	return rVal;
        
        return Comparer<string>.Default.Compare(Middle, other.Middle);
    }
    
    public bool Eqauls(Name other)
    {
    	if (ReferenceEquals(this, other))
       		return true;
        if (ReferenceEquals(other, null))
        	return false;
            
        return Last == other.Last &&
        	First == other.First &&
            Middle == other.Middle;
    }
}

제네릭 클래스가 된다면 상호 비교나 값의 동일성 체크 전에 타입의 동일성 체크가 먼저 이루어져야 하는데, 

해당 메서드는 제네릭 메서드로 정의해야한다.

public static bool CheckEquality<T>(T left, T right)
	where T : IEquatable<T>
{
    if (left == null)
        return right == null;

    return left.Equals(right);
}

논제네릭 클래스들을 위해서도 따로 타입 동일성 비교 함수를 구현해주어야 한다.

public override bool Equals(object obj)
{
    if (obj.GetType() == typeof(Name))
    	return this.Equals(obj as Name);
    else
    	return false;
}

Equals를 재정의 했다면, GetHashCode도 재정의하자

public override int GetHashCode()
{
    int hashCode = 0;
    
    if (Last != null)
    	hashCode ^= Last.GetHashCode();
    if (First != null)
    	hashCode ^= First.GetHashCode();
    if (Middle != null)
    	hashCode ^= Middle.GetHashCode();
        
   	return hashCode;
}

IEquality<T>를 구현하였다면,  operator==와 operator !=도 함께 구현되어야 한다.

public static bool operator ==(Name left, Name right)
{
    if (left == null)
    	return right == null;
        
    return left.Equals(right);
}

public static bool operator !=(Name left, Name right)
{
    if (left == null)
    	return right != null;
    
    return !left.Equals(right);
}

동등성 비교를 하였으니 이제 선후 관계 비교를 구현해보자.

public class Name : IComparable<Name>, IEquatable<Name>, IComparable
{
    int IComparable.CompareTo(object obj)
    {
    	if (obj.GetType() != typeof(Name))
        	throw new ArgumentException("Argument is not a Name object");
        
        return this.CompareTo(obj as Name);
    }
}

IComparable같은 논제네릭 타입의 인터페이스를 구현할 때 명시적인 인터페이스 구현 방식을 사용해야 한다.

다음의 코드들은 논제네릭방식의 메서드 구현이다.

public static bool operator <(Name left, Name right)
{
    if (left == null)
    	return right != null;
    
    return left.CompareTo(right) < 0;
}

public static bool operator >(Name left, Name right)
{
    if (left == null)
    	return false;
    
    return left.CompareTo(right) < 0;
}

선후 관계와 동일성 관계를 함께 정의하는 '<='와 '>=' 연산자도 구현해야 한다.

public static bool operator <=(Name left, Name right)
{
    if (left == null)
    	return true;
    
    return left.CompareTo(right) <= 0;
}

public static bool operator >=(Name left, Name right)
{
    if (left == null)
    	return right == null;
    
    return left.CompareTo(right) >= 0;
}

위의 메서드 구현방식을 참고하여 레거시 코드와 같이 사용되는 제네릭 클래스를 구현할 때 포함되는 인터페이스에

따라 메서드를 명시적으로 구현해주어야 한다.

Comments