Home Full Site
C# : struct와 class에서의 Eqaulity

C#에서 struct는 Stack에 저장하는 Value Type이고, class는 Heap에 저장하는 Reference Type이다.

Value Type과 Reference Type은 메모리의 위치 뿐만 아니라, 값을 비교하는 방식에도 차이를 보인다. Value Type인 struct는 두 인스턴스를 비교할 때 Equals() 메서드를 사용하는데, 두 인스턴스의 Type이 같고 그 값이 같다면 True를 리턴한다. Value Type을 비교할 때는 == Operator를 사용할 수 없다.

Reference Type인 class의 경우, 두 인스턴스의 Type이 같고 그 값이 같은 지를 체크하는 Equals() 메서드와 두 변수가 동일한 메모리를 가리키는 지 체크하는 == Operator를 사용한다. (class의 경우 (IEquatable of T).Equals() 인터페이스를 구현하여 인스턴스의 데이타가 같은 지를 체크하도록 한다. 만약 class가 IEquatable 인터페이스를 구현하지 않으면, (실제 데이타가 같더라도) Equals()는 False를 리턴한다)


예제

// (1) 두 MyData 구조체의 값이 같으므로 Equals()는 True를 리턴함
public struct MyData
{
    public int Category { get; set; }
    public String Name { get; set; }
}

MyData m = new MyData();
m.Category = 1;
m.Name = "Watch";

MyData m2 = new MyData();
m2.Category = 1;
m2.Name = "Watch";

bool same = m.Equals(m2); // True

// (2) MyData를 class로 바꾸고, IEquatable을 구현하면 m.Equals(m2)는 True를 리턴함.
// IEquatable을 구현하지 않으면  m.Equals(m2)는 False를 리턴함.
public class MyData : IEquatable<MyData>
{
    public int Category { get; set; }
    public String Name { get; set; }
    public bool Equals(MyData other) {
        return this.Category == other.Category && this.Name == other.Name;
    }
}

// (3) 레퍼런스가 동일한 경우 Equals() 혹은 ==은 True를 리턴함
var m2 = m;
bool same = m.Equals(m2); // True
same = m == m2; // True



C# : record class

C# 9에서 도입된 record 타입은 record class 타입으로도 불리우는데, class 레퍼런스 타입의 속성을 가지면서 필드값이 변하지 않는 Immuntable한 속성을 함께 갖는 타입이다. record class 타입은 Equals() 혹은 == Operator을 사용하여 두 인스턴스를 마치 Value Type인 것처럼 비교하게 된다. 즉, record 타입은 두 인스턴스가 같은 타입이고 그 필드 값들이 동일하면, Equals() 혹은 == Operator에서 True를 리턴한다.

예제

public record MyData
{
    public int Category { get; init; }
    public String Name { get; init; }
}

class Program
{
    static void Main(string[] args)
    {
        MyData m1 = new MyData { Category = 1, Name = "Watch" };
        MyData m2 = new MyData { Category = 1, Name = "Watch" };

        bool same1 = m1.Equals(m2); // true
        bool same2 = m1 == m2;  // true
    }
}



C# 10 : record struct

C# 10에서는 record struct 타입을 새로 도입하였는데, C# 9의 'record'를 C# 10에서는 'record' 혹은 'record class'로 정의할 수 있다. record class는 record 레퍼런스 타입이고, record struct는 record Value 타입이다. C# 9의 record 타입은 record class 타입인데, C# 9에서는 'record class'라고 쓸 수 없고, C# 10에서는 새로 도입된 record struct와 명시적으로 분리하기 위해, record class라는 키워드를 도입하였다.

struct 타입에서 동일성(Equality)을 체크하기 위해서는 Equals()를 사용하여 두 인스턴스의 값들이 같은 지를 체크하는데, record struct는 (record class와 마찬가지로) Equals()와 == Operator를 함께 사용할 수 있다. record class와 같이, record struct도 두 인스턴스의 필드값이 같으면 True를 리턴한다.


예제

public record struct MyData
{
    public int Category { get; init; }
    public int Id { get; set; }
}

class Program
{
    static void Main(string[] args)
    {
        MyData m1 = new MyData { Category = 1, Id = 100 };
        MyData m2 = new MyData { Category = 1, Id = 100 };
        bool same1 = m1.Equals(m2); // 두 값이 같으므로 true

        m1.Id = 200;
        bool diff = m1 == m2;  // Id가 변경되었으므로 false
    }
}



C# 10 : recode 타입에서 sealed ToString() 사용

C# 10에서는 record class 타입에서 ToString() 메서드를 정의할 때, sealed 키워드를 사용할 수 있게 되었다. sealed를 쓰는 효과는 해당 record class의 파생 타입에서 새로 ToString()을 정의하지 못하게 하고, 고정된 ToString() 메서드를 사용하도록 한다.

예제

var d = new MyDerived();
Console.WriteLine(d.ToString());

record class MyBase
{
    public int Id { get; set; } = 0;

    // C# 10: sealed 사용 가능
    public sealed override string ToString()
    {
        return $"{Id}";
    }
}

record class MyDerived : MyBase
{
    public int Category { get; set; }
}



© csharpstudy.com