다음을 통해 공유


튜플 및 무명 형식

튜플은 단일 구조의 여러 멤버에 대한 간단한 데이터 구조를 제공합니다. 익명 형식보다 선호하는 선택입니다. 튜플은 더 나은 성능을 제공하고 분해를 지원하며 보다 유연한 구문을 제공합니다.

익명 형식을 사용하면 먼저 명시적으로 형식을 정의할 필요 없이 읽기 전용 속성 집합을 단일 개체로 편리하게 캡슐화할 수 있습니다. 컴파일러는 형식 이름을 생성하며 소스 코드 수준에서 사용할 수 없습니다. 컴파일러는 각 속성의 형식을 유추합니다. 주로 식 트리 지원이 필요하거나 참조 형식이 필요한 코드를 사용할 때 익명 형식을 사용합니다.

튜플 대 무명 형식

튜플과 무명 형식을 모두 사용하면 명명된 형식을 정의하지 않고 여러 값을 그룹화할 수 있습니다. 그러나 튜플은 언어 지원이 향상되고 보다 효율적인 데이터 구조로 컴파일됩니다. 다음 표에는 주요 차이점이 요약되어 있습니다.

특징 무명 형식 튜플
유형 참조 형식(class) 값 형식(struct)
Performance 힙 할당 스택 할당(성능 향상)
Mutability 읽기 전용 속성 변경 가능한 필드
분해 지원되지 않음 지원됨
표현식 트리 지원됨 지원되지 않음
액세스 한정자 internal public
멤버 이름 필수 또는 유추 선택 사항(다음과 같은 Item1Item2기본 이름 포함)

튜플을 사용하는 경우

다음과 같은 경우 튜플을 사용합니다.

  • 스택 할당을 통해 더 나은 성능이 필요합니다.
  • 값을 별도의 변수로 분해하려고 합니다.
  • 메서드에서 여러 값을 반환합니다.
  • 식 트리 지원은 필요하지 않습니다.

다음 예제에서는 튜플이 더 깨끗한 구문을 사용하여 익명 형식과 유사한 기능을 제공하는 방법을 보여 줍니다.

// Tuple with named elements.
var tupleProduct = (Name: "Widget", Price: 19.99M);
Console.WriteLine($"Tuple: {tupleProduct.Name} costs ${tupleProduct.Price}");

// Equivalent example using anonymous types.
var anonymousProduct = new { Name = "Widget", Price = 19.99M };
Console.WriteLine($"Anonymous: {anonymousProduct.Name} costs ${anonymousProduct.Price}");

튜플 분해

튜플을 개별 변수로 분해하여 각 튜플 요소를 개별적으로 다루는 편리한 방법을 제공합니다. C#은 튜플을 분해하는 여러 가지 방법을 지원합니다.

static (string Name, int Age, string City) GetPersonInfo()
{
    return ("Alice", 30, "Seattle");
}
// Deconstruct using var for all variables
var (name, age, city) = GetPersonInfo();
Console.WriteLine($"{name} is {age} years old and lives in {city}");
// Output: Alice is 30 years old and lives in Seattle

// Deconstruct with explicit types
(string personName, int personAge, string personCity) = GetPersonInfo();
Console.WriteLine($"{personName}, {personAge}, {personCity}");

// Deconstruct into existing variables
string existingName;
int existingAge;
string existingCity;
(existingName, existingAge, existingCity) = GetPersonInfo();

// Deconstruct and discard unwanted values using the discard pattern (_)
var (name2, _, city2) = GetPersonInfo();
Console.WriteLine($"{name2} lives in {city2}");
// Output: Alice lives in Seattle

분해는 루프 및 패턴 일치 시나리오에서 유용합니다.

var people = new List<(string Name, int Age)>
{
    ("Bob", 25),
    ("Carol", 35),
    ("Dave", 40)
};

foreach (var (personName2, personAge2) in people)
{
    Console.WriteLine($"{personName2} is {personAge2} years old");
}

메서드 반환 형식으로서의 튜플

튜플의 일반적인 사용 사례는 메소드 반환 타입입니다. 매개 변수를 out 정의하는 대신 메서드 결과를 튜플로 그룹화할 수 있습니다. 이름이 없고 반환 형식을 선언할 수 없으므로 메서드에서 익명 형식을 반환할 수 없습니다.

다음 예제에서는 사전 조회와 함께 튜플을 사용하여 구성 범위를 반환하는 방법을 보여 줍니다.

var configLookup = new Dictionary<int, (int Min, int Max)>()
{
    [2] = (4, 10),
    [4] = (10, 20),
    [6] = (0, 23)
};

if (configLookup.TryGetValue(4, out (int Min, int Max) range))
{
    Console.WriteLine($"Found range: min is {range.Min}, max is {range.Max}");
}
// Output: Found range: min is 10, max is 20

이 패턴은 성공 표시기와 여러 결과 값을 모두 반환해야 하는 메서드를 사용할 때 유용합니다. 튜플을 사용하면 Item1Item2같은 제네릭 이름 대신 명명된 필드(MinMax)를 사용하여 코드를 더 읽기 쉽게 만들고 자체적으로 문서화할 수 있습니다.

익명 형식을 사용하는 경우

다음과 같은 경우 익명 형식을 사용합니다.

  • 식 트리(예: 일부 LINQ(Microsoft Language-Integrated Query) 공급자)로 작업하고 있습니다.
  • 개체가 참조 형식이어야 합니다.

가장 일반적인 시나리오는 다른 형식의 속성으로 익명 형식을 초기화하는 것입니다. 다음 예제에서는 Product라는 클래스가 있다고 가정합니다. 클래스 Product에는 ColorPrice 속성 외에도 당신이 관심 없는 다른 속성도 포함되어 있습니다.

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

무명 형식 선언은 new개체 이니셜라이저로 시작합니다. 선언에서는 Product의 두 속성만 사용하는 새 형식을 초기화합니다. 익명 형식은 일반적으로 쿼리 식 절에서 select 더 적은 양의 데이터를 반환하는 데 사용됩니다. 쿼리에 대한 자세한 내용은 C#의 LINQ를 참조하세요.

익명 형식에서 멤버 이름을 지정하지 않으면 컴파일러는 익명 형식 멤버에게 초기화하는 데 사용되는 속성과 동일한 이름을 제공합니다. 앞의 예제에 표시된 것처럼, 식으로 초기화되는 속성의 이름을 제공합니다.

다음 예제에서 익명 형식의 속성 이름은 ColorPrice입니다. 인스턴스는 형식 컬렉션의 productsProduct 항목입니다.

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);
}

무명 형식의 프로젝션 이니셜라이저

익명 형식은 멤버 이름을 명시적으로 지정하지 않고 직접 지역 변수 또는 매개 변수를 사용할 수 있는 프로젝션 이니셜라이저를 지원합니다. 컴파일러는 변수 이름의 멤버 이름을 유추합니다. 다음 예제에서는 이 간소화된 구문을 보여 줍니다.

// Explicit member names.
var personExplicit = new { FirstName = "Kyle", LastName = "Mit" };

// Projection initializers (inferred member names).
var firstName = "Kyle";
var lastName = "Mit";
var personInferred = new { firstName, lastName };

// Both create equivalent anonymous types with the same property names.
Console.WriteLine($"Explicit: {personExplicit.FirstName} {personExplicit.LastName}");
Console.WriteLine($"Inferred: {personInferred.firstName} {personInferred.lastName}");

이 간소화된 구문은 다음과 같은 여러 속성을 사용하여 익명 형식을 만들 때 유용합니다.

var title = "Software Engineer";
var department = "Engineering";
var salary = 75000;

// Using projection initializers.
var employee = new { title, department, salary };

// Equivalent to explicit syntax:
// var employee = new { title = title, department = department, salary = salary };

Console.WriteLine($"Title: {employee.title}, Department: {employee.department}, Salary: {employee.salary}");

멤버 이름은 다음 경우에 유추되지 않습니다.

  • 후보 이름은 명시적 또는 암시적 같은 익명 형식의 다른 속성 멤버를 복제합니다.
  • 후보 이름은 유효한 식별자가 아닙니다(예: 공백 또는 특수 문자 포함).

이러한 경우 멤버 이름을 명시적으로 지정해야 합니다.

.NET 스타일 규칙 IDE0037을 사용하여 유추된 멤버 이름 또는 명시적 멤버 이름 중 어느 것이 선호되는지를 적용할 수 있습니다.

클래스, 구조체 또는 다른 익명 형식과 같은 다른 형식의 개체를 사용하여 필드를 정의할 수도 있습니다. 이렇게 하려면 이 개체를 보유하는 변수를 사용합니다. 다음 예제에서는 이미 인스턴스화된 사용자 정의 형식을 사용하는 두 개의 익명 형식을 보여줍니다. 두 경우 모두, 무명 형식 shipmentshipmentWithBonusproduct 필드는 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에 대한 자세한 내용은 암시적 형식 지역 변수를 참조하세요.

다음 예제에 표시된 것처럼, 암시적으로 형식화된 지역 변수와 암시적으로 형식화된 배열을 결합하여 익명으로 형식화된 요소의 배열을 만들 수 있습니다.

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

무명 형식은 class에서 직접 object로 파생되는 형식이며, object를 제외한 다른 형식으로는 캐스팅할 수 없습니다. 컴파일러는 애플리케이션에서 액세스할 수 없지만 각 익명 형식에 대한 이름을 제공합니다. 공용 언어 런타임의 관점에서 익명 형식은 다른 참조 형식과 다를 바가 없습니다.

어셈블리에서 둘 이상의 익명 개체 이니셜라이저가 순서와 이름 및 형식이 동일한 속성의 시퀀스를 지정하는 경우 컴파일러는 개체를 동일한 형식의 인스턴스로 처리합니다. 이러한 개체는 컴파일러에서 생성된 동일한 형식 정보를 공유합니다.

무명 형식은 with 식 형태로 비파괴적 변경을 지원합니다. 이 기능을 사용하면 하나 이상의 속성에 새 값이 있는 익명 형식의 새 인스턴스를 만들 수 있습니다.

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

익명 형식을 갖는 것으로 메서드의 필드, 속성, 이벤트 또는 반환 형식을 선언할 수 없습니다. 마찬가지로 메서드, 속성, 생성자 또는 인덱서의 공식 매개 변수를 익명 형식으로 선언할 수 없습니다. 무명 형식이나 무명 형식이 포함된 컬렉션을 메서드에 인수로 전달하려면 매개 변수를 object 형식으로 선언하면 됩니다. 그러나 무명 형식에 object를 사용하면 강력한 형식화의 목적이 무효화됩니다. 쿼리 결과를 저장하거나 메서드 경계 외부로 전달해야 하는 경우 익명 형식 대신 일반적인 명명된 구조체 또는 클래스 사용을 고려하세요.

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

참고 항목

익명 형식의 입니다. 따라서 서로 다른 어셈블리에 정의된 두 익명 형식은 동일한 형식이 아닙니다. 따라서 익명 형식의 인스턴스는 모든 속성이 같은 경우에도 서로 다른 어셈블리에 정의된 경우 서로 같을 수 없습니다.

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

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

Console.WriteLine(v.ToString()); // "{ Title = Hello, Age = 24 }"

참고하십시오