[C#] 8.C#의 네임스페이스, 클래스, 레코드, 인터페이스, 제네릭, 익명 형식

1. 형식을 구성하도록 네임스페이스 선언

 

네임스페이스는 C# 프로그래밍에서 두 가지 방법으로 많이 사용됩니다. 먼저 .NET은 다음과 같이 네임스페이스를 사용해 여러 클래스를 구성합니다. 

System.Console.WriteLine("Hello World");

 

System은 네임스페이스며 Console은 해당 네임스페이스의 클래스입니다. 다음 예제에서 전체 이름이 필요하지 않도록 using 키워드를 사용할 수 있습니다.

using System;

Console.WriteLine("Hello World");

 

두 번째로는 고유한 네임스페이스를 선언하면 대규모 프로그래밍 프로젝트에 클래스 및 메서드 이름의 범위를 제어할 수 있습니다. 네임스페이스 키워드를 사용해 네임스페이스를 선언합니다.

namespace SampleNameSpace
{
    class NameClass
    {
        public void NameMethod()
        {
            System.Console.WriteLine("SampleMethod inside NameSpaceMethod");
        }
    }
}

 

네임스페이스 이름은 유효한 C# 식별자 이름이어야합니다.

namespace SampleNamespace;

class AnotherSampleClass
{
    public void AnotherSampleMethod()
    {
        System.Console.WriteLine(
            "SampleMethod inside SampleNamespace");
    }
}

 

이 새로운 구문의 장점은 가로 공관과 중괄호를 절약해 더 간단하다는 것입니다.

 

2. 클래스

 

class로 정의된 형식은 참조 형식입니다. 런타임에 참조 형식의 변수를 선언하면 new 연산자를 사용해 클래스의 인스턴스를 명시적으로 만들거나 다음 예제와 같이 다른 곳에서 만들어진 호환성 있는 형식의 개체를 할당할 때까지 변수에는 null 값이 포함됩니다.

using namespace 

MyClass mc = new MyClass();

 

클래스 선언

 

클래스는 다음과 같이 class 키워드와 뒤에 고유 식별자를 사용하면 선언됩니다.

public class Customer
{

}

 

선택적 엑세스 한정자는 class 키워드 앞에 옵니다. class 형식의 기본 엑세스는 internal입니다. 이 경우 public(이) 사용되므로 누구나 이 클래스의 인스턴스를 만들 수 있습니다. 클래스 이름은 class 키워드 뒤에 옵니다. 

 

개체 만들기

 

서로 같은 의미로 사용되는 경우도 있지만 클래스와 개체는 서로 다릅니다. 클래스는 개체의 형식을 정의하지만 개체 자체는 아닙니다. 개체는 클래스에 기반을 둔 구체적 엔터티이고 클래스의 인스턴스라고도 합니다.

Customer object1 = new Customer();

 

클래스 인스턴스가 만들어질 때 개체에 대한 참조가 다시 프로그래머에게 전달됩니다. 

Customer object2;

 

이러한 참조를 통해 개체에 엑세스하려고 하면 런타임에 실패하므로 개체를 참조하지 않는 개체 참조를 만들지 않는 것이 좋습니다. 새 개체를 만들거나 다음과 같이 기존 개체를 할당해 개체를 참조하는 참조를 만들 수 있습니다.

Customer object3 = new Customer();
Customer object4 = object3;

 

이 코드에서는 같은 개체를 참조하는 두 개의 개체 참조를 만듭니다. 따라서 object3을 통해 이루어진 모든 개체 변경 내용은 이후 object4 사용시 반영됩니다.

 

생성자 및 초기화

 

형식의 인스턴스를 만들 때 해당 필드와 속성이 유용한 값으로 초기화되었는지 확인하려 합니다. 값을 초기화하는 방법에는 여러가지가 있습니다.

  • 기본값 수용
  • 필드 이니셜라이저
  • 생성자 매개 변수
  • 개체 이니셀라이저

모든 .NET 형식에 기본값이 있으며, 일반적으로 이 값은 숫자 형식의 경우 0이며 모든 참조 형식에 null 값을 가집니다. 앱에서 적절한 경우 기본값으로 사용할 수 있습니다. .NET 기본값이 올바른 값이 아닌 경우 필드 이니셜라이저를 사용해 초기 값을 설정할 수 있습니다.

public class Container
{
    private int _capacity = 10;
}

 

호출자가 초기 값을 설정하는 생성자를 정의해 초기 값을 제공하도록 요구할 수 있습니다.

public class Container
{
    private int _capacity = 10;

    public Container(int capacity) => _capacity = capacity;
}

 

C# 12부터 클래스 선언의 일부로 기본 생성자를 정의할 수 있습니다.

public class Container(int capacity)
{
    //private int _capacity = 10;

    //public Container(int capacity) => _capacity = capacity;

    public int _capacity = capacity;
}

 

클래스 이름에 매게 변수를 추가 시 기본 생성자로 정의되며, 이러한 매개변수는 해당 맴버를 포함하는 클래스 본문에서 사용할 수 있습니다. 이를 사용해 필드 또는 필요한 다른 곳을 초기화할 수 있습니다.

 

속성에 required 한정자를 사용하고 호출자가 개체 이니셜라이저를 사용하여 속성의 초기 값을 설정하도록 허용할 수 있습니다.

public class Person
{
    public required string LastName {  get: set; }
    public required string FirstName {  get: set; }
}

 

required 키워드를 추가하면 호출자가 해당 속성을 new 식의 일부로 설정해야 합니다.

var p1 = new Person(); 
var p2 = new Person() { FirstName FirstName: 'Grade', LastName = "Hopper" };

 

클래스 상속

 

클래스는 개체 지향 프로그래밍의 기본적 특성인 "상속"을 완전히 지원합니다. 클래스를 만들 때 sealed(으)로 정의되지 않은 다른 클래스에서 상속할 수 있습니다. 다른 클래스는 클래스에서 상속하고 클래스 가상 메서드를 재정의할 수있습니다.또한 하나 이상의 인터페이스를 구현할 수 있습니다.

 

상속은 파생을 통해 수행합니다. 즉, 클래스는 데이터와 동작을 상속하는 소스 기본 클래스를 사용하여 지원됩니다. 다음과 같이 파생 클래스 이름 뒤에 콜론 및 기본 클래스 이름을 추가해 기본 클래스를 지정합니다.

public class Manage : Employee
{

}

 

3. 레코드 

 

C#의 레코드는 데이터 모델 작업에 특별한 구문과 동작을 제공하는 클래스 또는 구조체입니다. record 한정자는 주 역할이 데이터를 저장하는 형식에 유용한 맴버를 합성하도록 컴파일러에 지시합니다. 이러한 맴버에는 값 같음을 지원하는 ToString() 및 맴버의 오버로드가 포함됩니다.

 

# 레코드를 사용하는 경우

 

값 같음

 

레코드의 경우 값이 같음은 형식이 일치하고 모든 속성 및 필드 값이 동일하다고 비교될 경우 레코드 형식의 두 변수가 같다는 것을 의미합니다. 클래스와 같은 다른 참조 형식의 경우 값 같음이 구현되지 않는 한 기본적으로 참조 같음을 의미합니다. 즉 클래스 형식의 두 변수는 같은 개체를 참조하는 경우 같습니다. 두 레코드 인스턴스의 같음을 확인하는 메서드와 연산자에서 값 같음을 사용합니다.

 

불변성

 

클래스 또는 구조체를 선언하고 인스턴스화하는 동일한 구문을 레코드와 함께 사용할 수 있습니다. class 키워드를 record로 대체하거나 struct 대신 record struct를 사용합니다. 마찬가지로 상속 관계를 표현하기 위한 동일한 구문은 레코드 클래스에서 지원됩니다. 레코드가 클래스와 다른 점은 다음과 같습니다.

 

  • 기본 생성자에서 위치 매개 변수를 사용해 변경할 수 없는 속성을 사용하여 형식을 만들고 인스턴스화 가능
  • 클래스에서 참조 같음 또는 같지 않음(예:Object.Equals(Object) 및 ==)을 나타내는 동일한 메서드와 연산자가 레코드에서 값 같음 또는 같지 않음을 나타냅니다.
  • with 식을 사용하여 선택된 속성에 새 값을 포함하는 변경할 수 없는 개체의 복사본을 만들 수 있음
  • 레코드의 ToString 매서드는 개체 형식 이름과 모든 퍼블릭 속성의 이름과 값을 표시하는 형식 문자열을 만듬
  • 레코드는 다른 레코드에서 상속될 수 있음. 레코드는 클래스에서 상속될 수 없으며 클래스는 레코드에서 상속될수 없음.

레코드 구조체는 컴파일러가 같음과 ToString을 위한 메서드를 합성한다는 점에서 구조체와 다름니다. 컴파일러는 위치 레코드 구조체에 대한 Doconstruct 메서드를 합성합니다.

 

컴파일러는 record class의 각 기본 생성자 매개 변수에 대한 공개 초기화 전용 속성을 합성합니다. record struct에서 컴파일러는 공개 읽기/쓰기 속성을 합성합니다. 컴파일러는 record 한정자를 포함하지 않는 class 및 struct 형식에서 기본 생성자 매개 변수에 대한 속성을 만들지 않습니다.

public record Person(string FirstName, string LastName);

public static class Program
{
    public static void Main()
    {
        Person person = new("Nancy", "Davolio");
        Console.WriteLine(person);
    }
}

 

다음 예제에는 레코드에서 값 같음을 보여 줍니다.

public record Person(string FirstName, string LastName, string[] PhoneNumbers);
public static class Program
{
    public static void Main()
    {
        var phoneNumbers = new string[2];
        Person person = new("Nancy", "Davolio", phoneNumbers);
        Person person2 = new("Nancy", "Davolio", phoneNumbers);
        Console.WriteLine(person == person2);

        person.PhoneNumbers[0] = "342-2254";
        Console.WriteLine(person == person2);

        Console.WriteLine(ReferenceEquals(person, person2));
        
    }
}

 

다음은 with 식을 사용하여 변경할 수 없는 개체를 복사하고 속성 중 하나를 변경하는 방법을 보여줍니다.

 

public record Person(string FirstName, string LastName)
{
    public required string[] PhonNumbers { get; init; }
}
public static class Program
{
    public static void Main()
    {
        Person person1 = new("Nancy", "Davolio") { PhonNumbers = new string[1] };
        Console.WriteLine(person1);

        Person person2 = person1 with { FirstName = "John" };
        Console.WriteLine(person2);
        Console.WriteLine(person1 == person2);

        person2 = person1 with { PhonNumbers = new string[1] };
        Console.WriteLine(person2);
        Console.WriteLine(person1 == person2);

        person2 = person1 with { };
        Console.WriteLine(person1 == person2);
    }
}

 

4. 인터페이스 - 여러 형식에 대한 동작 정의

 

인터페이스에서 비추상 클래스 class 또는 struct가 구현해야 하는 관련 기능 그룹에 대한 정의가 포함되어 있습니다. 인터페이스에서는 구현이 있어야 하는 static 매서드를 정의할 수 있습니다. 인터페이스는 맴버에 대한 기본 구현을 정의할 수 있습니다. 인터페이스는 필드, 자동으로 구현된 속성 또는 속성과 같은 이벤트와 같은 인터페이스 데이터를 선언할 수 없습니다. 

 

interface IEquatable<T>
{
    bool Equals(T obj);
}

 

인터페이스 이름은 유효한 C# 식별자 이름이어야 합니다. 규칙에 따라 인터페이스 이름은 대문자 T로 시작되며 IEquatable<T> 인터페이스를 구현하는 모든 클래스나 구조체에는 인터페이스에서 지정한 서명과 일치하는 Equals 메서드에 정의가 포함되어 있어야합니다. 

 

결과적으로 IEquatable<T>를 구현하는 T 유형의 클래스는 이 클래스의 인스턴스가 같은 클래스의 다른 인스턴스와 동일한지 여부를 확인할 수 있는 Equals 매서드를 포함할 수 있습니다. IEquatable<T>의 정의에서는 Equals에 대한 구현을 제공하지 않습니다. 클래스 또는 구조체는 여러 인터페이스를 구현할 수 있지만 클래스는 단일 클래스에서만 상속됩니다.

 

추상/봉인된 클래스 및 클래스 멤버 - C#

C#의 추상 키워드를 사용하여 불완전한 클래스와 클래스 멤버를 만듭니다. Sealed 키워드는 이전 가상 클래스 또는 클래스 멤버의 상속을 방지합니다.

learn.microsoft.com

 

인터페이스에는 인스턴스 메서드, 속성, 이벤트, 인덱서 또는 이러한 네 가지 멤버 형식의 조합이 포함될 수 있습니다. 인터페이스에는 정적 생성자, 필드, 상수 또는 연산자가 포함될 수 있습니다. C# 11부터 필드가 아닌 인터페이스 맴버는 static abstract일 수 있고, 인터페이스에는 인스턴스 필드, 인스턴스 생성자 또는 종료자가 포함될 수 있습니다.

 

인터페이스 맴버는 기본적으로 공용이며, public, protected, internal, private, protected internal 또는 private protected 등의 접근성 한정자를 명시적 지정할 수 있습니다. private 맴버에는 기본 구현이 있어야 합니다.

 

인터페이스 맴버를 구현하려면 구현 클래스의 해당 맴버가 공용이고 비정적이어야 하며 인터페이스 맴버와 동일한 이름 및 서명을 사용해야 합니다. 인터페이스를 구현하는 클래스 또는 구조체는 인터페이스에서 제공하는 기본 구현 없이 선언된 모든 맴버에 대한 구현을 제공해야 합니다.

 

그러나 기본 클래스에서 인터페이스를 구현해야 하는 경우 기본 클래스에서 파생되는 모든 클래스가 해당 구현을 상속합니다. 다음 예제는 IEquatable<T> 인터페이스의 구현을 보여줍니다.

public class Car : IEquatable<Car>
{
    public string? Make { get; set; }
    public string? Model { get; set; }
    public string? Year { get; set; }

    public bool Equals(Car? car)
    {
        return (this.Make, this.Model, this.Year) == (car?.Make, car?.Model, car?.Year);
    }
}

 

클래스의 속성 및 인덱서는 인터페이스에 정의된 속성이나 인덱서에 대해 추가 접근자를 정의할 수 있습니다. 예를 들어 인터페이스는 get 접근자가 있는 속성을 선언할 수 있습니다. 인터페이스를 구현하는 클래스는 get 및 set 접근자를 둘 다 사용하는 동일한 속성을 선언할 수 있습니다.

 

그러나 속성 또는 인덱서에서 명시적 구현을 사용하는 경우에는 접근자가 일치해야 합니다. 인터페이스는 하나 이상의 인터페이스에서 상속할 수 있습니다. 파생 인터페이스는 기본 인터페이스에서 맴버를 상속합니다. 파생 인터페이스를 구현하는 클래스는 파생 인터페이스의 기본 인터페이스 맴버 모두를 포함해 파생 인터페이스의 모든 맴버를 구현해야 합니다.

 

이 클래스는 파생 인터페이스나 해당하는 기본 인터페이스로 암시적으로 변환될 수 있습니다. 클래스는 상속하는 기본 클래스 또는 다른 인터페이스에서 상속하는 인터페이스를 통해 인터페이스를 여러 번 포함할 수 있습니다. 그러나 클래스는 인터페이스의 구현을 한 번만 제공할 수 있으며 클래스가 인터페이스를 클래스 정의의 일부로 선언하는 경우에만 제공할 수 있습니다.

 

인터페이스를 구현하는 기본 클래스를 상속했기 때문에 인터페이스가 상속되는 경우 기본 클래스는 인터페이스 맴버 구현을 제공합니다. 그러나 파생 클래스는 상속된 구현을 사용 하는 대신 가상 인터페이스 맴버를 다시 구현할 수 있습니다. 인터페이스가 매서드의 기본 구현을 선언하면 해당 인터페이스를 구현하는 모든 클래스가 해당 구현을 상속합니다.

 

또한 기본 클래스는 가상 맴버를 사용하여 인터페이스 맴버를 구현할 수 있습니다.

 

5. 재네릭 클래스 및 메서드

 

제네릭은 .NET에 형식 매개 변수의 개념을 소개합니다. 제네릭을 사용하면 코드에서 클래스 또는 메서드를 사용할 때까지 하나 이상의 형식 매개 변수 사양을 연기하는 클래스와 메서드를 디자인할 수 있습니다. 예를 들어 제네릭 형식 매개 변수 T를 사용하여 여기에 표시된 것처럼, 다른 클라이언트 코드에서 런타임 캐스팅 또는 boxing 작업에 대한 비용이나 위험을 발생하지 않고 사용할 수 있는 단일 클래스를 작성할 수 있습니다.

public class GenericList<T>
{
    public void Add(T item) { }
}

public class ExampleClass { }

class GenericListLine
{
    static void Main()
    {
        GenericList<int> list1 = new();
        list1.Add(1);

        GenericList<string> list2 = new();
        list2.Add("");

        GenericList<ExampleClass> list3 = new();
        list3.Add(new ExampleClass());
    }
}

 

제네릭 클래스와 메서드는 재사용성, 형식 안정성 및 효율성을 제네릭이 아닌 클래스에서 사용할 수 없는 방식으로 결합합니다. 재네릭 형식 매개 변수는 컴파일 중에서 형식 인수로 대체됩니다. 제네릭은 컬렉션 및 해당 컬렉션에서 작동하는 메서드에서 가장 자주 사용됩니다. 

 

사용자 지정 제네릭 형식 및 메서드를 만들어 형식이 안전하고 효율적인 일반화된 솔루션 및 디자인 패턴을 직접 제공할 수도 있습니다. 다음 코드 예제에서는 데모용으로 간단한 제네릭 연결된 목록 클래스를 보여줍니다.(대부분의 경우 직접 만드는 대신 .NET에서 제공하는 클래스를 사용해야 List<T>합니다.) 형식 매게 변수 T는 구체적 형식이 일반적으로 목록에 저장된 항목의 형식을 나타내는 데 사용되는 여러 위치에서 사용됩니다.

 

  • AddHead 매서드에서 매서드 매개 변수의 형식.
  • 중첩  Node 클래스에서 Data 속성의 반환 형식.
  • 중첩 클래스에서 private 맴버 data의 형식

T는 중첩된 Node 클래스에 사용할 수 있습니다. GenericList<T> 예를 들어 구체적 형식으로 GenericList<int> 인스턴스화 되면 각 발생 T 항목이 int로 바뀝니다.

public class GenericList<T>
{
    private class Node(T t)
    {
        public T Data { get; set; } = t;

        public Node? Next { get; set; }
    }


    private Node? head;

    public void AddHead(T t)
    {
        Node n = new(t);
        n.Next = head;
        head = n;
    }

    public IEnumerator<T> GetEnumerator()
    {
        Node? current = head;

        while(current is not null)
        {
            yield return current.Data;
            current = current.Next;
        }
    }
}

 

다음 코드 예제에서는 클라이언트 코드에서 제네릭 GenericList<T> 클래스를 사용하여 정수 목록을 만드는 방법을 보여줍니다. 형식 인수를 변경하는 경우 다음 코드는 문자열 목록 또는 다른 사용자 지정 형식을 만듭니다.

    GenericList<int> list = new();
    for (int x = 0; x < 10; x++){
       list.AddHead(x);     
      
    }

    foreach(int i in list)
    {
    Console.WriteLine(i);
    }

    Console.WriteLine("Done");

 

6. 익명 형식

 

익명 형식을 사용하면 먼저 명시적 형식을 정의할 필요 없이 읽기 전용 속성 집합을 단일 개체로 편리하게 캡슐화할 수 있습니다. 형식 이름은 컴파일러에 의해 생성되며 소스 코드 수준에서 사용할 수 없습니다. 각 속성의 형식은 컴파일러에서 유추합니다. new 연산자를 개체 이니셜라이져와 함께 사용해 무명 형식을 만듭니다.

var v = new { Amount = 108, Massage = "Hi" };

 

일반적으로 무명 형식은 소스 시퀀스에 있는 각 개체의 속성 하위 집합을 반환하기 위해 쿼리 식의 select 절에 사용됩니다. 익명 형식은 하나 이상의 public 읽기 전용 속성을 포함합니다. 메서드 또는 이벤트와 같은 다른 종류의 클래스 맴버는 유효하지 않습니다. 속성을 초기화하는 데 사용되는 식은 null, 익명 함수 또는 포인터 형식일 수 없습니다.

 

가장 일반적인 시나리오는 다른 형식의 속성으로 익명 형식을 초기화하는 것입니다. 다음 예제는 Product라는 클래스가 있다고 가정하고, 클래스에는 Color 관심 없는 다른 속성과 함께 포함 product 및 price 속성이 포함됩니다.

class Product
{
    public string? Color { get; set; }
    public decimal Price { get; set; }
    public string? Name { get; set; }
    public string? Category { get; set; }

    public string? Size { get; set; }

}

 

익명 형식 선언은 new 키워드로 시작합니다. 선언에서 Product의 두 속성만 사용하는 새 형식을 초기화합니다. 무명 형식을 사용하면 더 적은 양의 데이터가 쿼리에 반환됩니다. 무명 형식에 맴버 이름을 지정하지 않으면 컴파일러가 무명 형식 맴버에 해당 맴버를 초기화하는 데 사용되는 속성과 동일한 이름을 제공합니다. 앞에 예제에 표신된 것처럼, 식으로 초기화 되는 속성의 이름을 제공하고, 다음 예제에서 익명 형식의 속성 이름은 Color 및 Price 입니다. 인스턴스는 형식 컬렉션의 products, Product 항목입니다.

var productQuery = from prod in products select new { prod.color, prod.price };

foreach ( var v in productQuery)
{
    Console.WriteLine("Color={0}, Price={1}", v.Color, v.Price);
}

 

클래스 구조체 또는 다른 무명 형식과 같은 다른 형식의 개체별로 필드를 정의할 수도 있습니다. 이 작업은 이미 인스턴스화된 사용자 정의 형식을 사용하여 두 개의 무명 형식이 만들어지는 다음 예제와 마찬가지로 이 개체를 보유하는 변수를 사용하여 수행됩니다. 두 경우 product 모두 무명 형식 shipment 의 필드이며 shipmentWithBonus 각 필드의 기본값을 포함하는 형식 Product입니다. 그리고 bonus 필드는 컴파일러에서 만든 무명 형식이 됩니다.

var product = new Product();
var bonus = new { note = "You Won!" };
var shipment = new { address = "Nowhere St.", product };
var shipmentWithBonus = new { address = "SomeWhere St.", product, bonus };

 

무명 형식을 사용하여 변수를 초기화 할때는 var을 사용하여 변수를 암시적 형식 지역 변수로 선언합니다. 컴파일러만 익명 형식의 기본 이름에 액세스할 수 있으므로 변수 선언에는 형식 이름을 지정할 수 없습니다. 다음 예제에서 표시된 것처럼. 암시적으로 형식화된 지역 변수와 암시적으로 형식화된 배열을 결합해 익명으로 형식화된 요소의 배열을 만들 수 있습니다.

var anonArray = new[] { new { name = "Chickin", diam = 1 }, new { name = "grape", diam = 1 } };

 

무명 형식은 object에서 직접 파생되고 object를 제외한 어떠한 형식으로도 캐스팅될 수 없는 class 형식입니다. 컴파일러는 애플리케이션에서 해당 익명 형식에 엑세스할 수 없더라도 각 익명 형식의 이름을 제공합니다. 공용 언어 런타임의 관점에서 익명 형식은 다른 참조 형식과 다를 바 없습니다.

 

어셈블리에서 둘 이상의 익명 개체 이니셜라이저가 순서와 이름 및 형식이 동일한 속성의 시퀀스를 지정하는 경우 컴파일러는 개체를 동일한 형식의 인스턴스로 처리합니다. 이러한 개체는 컴파일러에서 생성된 동일한 형식 정보를 공유합니다.무명 형식은 with 식 형태로 비파괴적 변경을 지원합니다. 이로 인해 하나 이상의 속성에 새 값이 있는 무명 형식의 새 인스턴스를 만들 수 있습니다.

var apple = new { Item = "apples", Price = 1.35 };
var onSale = apple with { Price = 0.50};
Console.WriteLine(apple);
Console.WriteLine(onSale);

 

익명 형식을 가지고 있으므로 필드, 속성, 이벤트 또는 메서드의 반환 형식은 선언할 수 없습니다. 마찬가지로, 익명 형식을 가지고 있으므로 메서드, 속성, 생성자 또는 인덱서의 정식 매개 변수는 선언할 수 없습니다. 무명 형식이나 무명 형식이 포함된 컬렉션을 메서드에 인수로 전달하려면 매개 변수를 object 형식으로 선언하면 됩니다.

 

그러나 무명 형식에 object를 사용하면 강력한 형식화의 목적이 무효화됩니다. 쿼리 결과를 저장하거나 메서드 경계 외부로 전달해야 하는 경우 익명 형식 대신 일반적인 명명된 구조체 또는 클래스 사용을 고려해야 합니다.

 

익명 형식에 대한 EqualsGetHashCode 매서드는 속성의 Equals 및 GetHashCode 메서드 측면에서 정의되므로 동일한 익명 형식의 두 인스턴스는 해당 속성이 모두 동일한 경우에만 동일합니다.

 

무명 형식은 ToString 메서드를 재정의하여 중괄호로 둘러싸인 모든 속성의 이름과 ToString 출력을 연결합니다.

var v = new { Title = "Hello", Age = 24 };

Console.WriteLine(v.ToString());

 

참고 자료

 

 

C# 형식 시스템 - C#

튜플, 레코드, 값 형식 및 참조 형식과 같은 C#의 형식 생성에 대해 알아봅니다.

learn.microsoft.com

 

 

GitHub - Koras02/project

Contribute to Koras02/project development by creating an account on GitHub.

github.com

 

LIST