"Item1", "Item2"보다 Tuple 클래스에서 더 나은 이름 지정


204

Tuple 클래스를 사용하는 방법이 있지만 그 안에 항목의 이름을 제공합니까?

예를 들면 다음과 같습니다.

public Tuple<int, int, int int> GetOrderRelatedIds()

OrderGroupId, OrderTypeId, OrderSubTypeId 및 OrderRequirementId의 ID를 리턴합니다.

내 방법의 사용자에게 어느 것이 무엇인지 알려주는 것이 좋을 것입니다. (메소드를 호출하면 결과는 result.Item1, result.Item2, result.Item3, result.Item4입니다. 어느 것이 어느 것인지 명확하지 않습니다.)

(나는이 모든 ID를 보유 할 클래스를 만들 수 있다는 것을 알고 있지만이 ID에는 이미 자체 클래스가 있으며이 메소드의 반환 값에 대한 클래스를 만드는 것은 어리석은 것처럼 보입니다.)


1
당신은 당신 자신을 굴려야 할 것입니다- Tuple매우 일반적인 것입니다
그것이

아니요, 그렇게 할 수 없습니다. 자세한 내용은이 링크를 참조하십시오. msdn.microsoft.com/en-us/vcsharp/ee957397
Enigma State

1
귀하의 API에 대한 공개 데이터 유형으로 Tuple을 사용하는 것은 권장되지 않을 것이라고 말하고 싶습니다. 나는 일반적으로 API의 반환 값이 아닌 수명이 짧은 내부 작업에 Tuple을 사용합니다.
Mike Burdick


4
C # 7의 작업 목록에 있습니다. github.com/dotnet/roslyn/issues/347
Philip Ding

답변:


277

C # 7.0 (Visual Studio 2017)에는이를위한 새로운 구성이 있습니다.

(string first, string middle, string last) LookupName(long id)

68
구문은 List<(int first, int second)>입니다. Visual Studio 2017에서 작동하게하려면 NuGet에서 System.ValueTuple 패키지를 다운로드해야했습니다.
Matt Davis

14
가치를 창조하려면return (first: first, middle: middle, last: last);
피아트

4
또는 return (first, middle, last);.NET 4.7.1에서 (4.7.0에 대해 확실하지 않음)
watbywbarif

1
그것을 사용하려면 System.ValueTuple nuget 패키지를 추가해야합니다
Alex G

11
C # 7 ValueTuple은 일반적으로 크지 만 변경 가능한 값 유형 (struct)이고 Tuple변경 불가능한 참조 유형 (클래스)입니다. 내가 아는 Tuple한 친숙한 항목 이름으로 참조 유형을 얻는 방법은 없습니다 .
dx_over_dt

51

C # 7.0까지는 자체 유형을 정의하는 데 짧은 방법이 없었습니다.


13
나는이 답변이 40 점으로 받아 들여 졌다고 믿을 수 없다. 최소한 적절한 생성자를 가진 클래스가 이것을 어떻게 대체 할 수 있는지 보여줄 수있다.
bytecode77

1
@ bytecode77 글쎄, 곧이 답변은 똑바로 잘못 될 것입니다 : github.com/dotnet/roslyn/issues/347
MarkPflug

이 언어 축제 제안을 보았습니다. 그러나 지금까지 클래스는 더 복잡한 데이터 유형에 적합한 유일한 솔루션입니다. 왜 상관없이 튜플을 강제로 사용하고 싶습니까 (다른 답변 참조)
bytecode77

3
C # 7이 릴리스 된 후에는 다음과 같은 작업을 수행 할 수 있습니다. msdn.microsoft.com/en-us/magazine/mt595758.aspx
Burak Karakuş

11
Q '에는 태그로 c # 4가 있으므로이 답변은 짧지 만 여전히 정확합니다.
Steve Drake

33

당신이 요구하는 것의 지나치게 복잡한 버전은 다음과 같습니다.

class MyTuple : Tuple<int, int>
{
    public MyTuple(int one, int two)
        :base(one, two)
    {

    }

    public int OrderGroupId { get{ return this.Item1; } }
    public int OrderTypeId { get{ return this.Item2; } }

}

왜 수업을하지 않습니까?


2
이 경우 클래스 대신 구조체가 더 좋을까요?
deathrace

5
내가 볼 수있는 약간의 장점은 항목이 모두 같은 경우 2 개의 인스턴스가 동일한 지 확인하여 equals 연산자를 자동으로 구현한다는 것입니다.
JSoet

8
이 방법의 또 다른 단점은 항목 1과 항목 2는 MyTuple에 공용 속성이 여전히 있다는 것입니다
RJFalconer

3
@deathrace Tuple 자체는 클래스이므로 직접 상속 받으려면 Tuple<T, T2>구조체가 될 수 없습니다.
차크라 바

3
내가 틀렸을 수도 있지만 객체를 반환하고 싶지만 특정 클래스를 정의하고 싶지 않은 곳에서는 주로 튜플을 사용합니다.
Jay

12

.net 4를 사용하면 아마도을 볼 수 ExpandoObject는 있지만 컴파일 타임 오류가 런타임 오류가 된 것처럼 간단한 경우에는 사용하지 마십시오.

class Program
{
    static void Main(string[] args)
    {
        dynamic employee, manager;

        employee = new ExpandoObject();
        employee.Name = "John Smith";
        employee.Age = 33;

        manager = new ExpandoObject();
        manager.Name = "Allison Brown";
        manager.Age = 42;
        manager.TeamSize = 10;

        WritePerson(manager);
        WritePerson(employee);
    }
    private static void WritePerson(dynamic person)
    {
        Console.WriteLine("{0} is {1} years old.",
                          person.Name, person.Age);
        // The following statement causes an exception
        // if you pass the employee object.
        // Console.WriteLine("Manages {0} people", person.TeamSize);
    }
}
// This code example produces the following output:
// John Smith is 33 years old.
// Allison Brown is 42 years old.

언급할만한 가치가있는 것은 메소드 내에서 익명 유형 이지만 클래스를 반환하려면 클래스를 만들어야합니다.

var MyStuff = new
    {
        PropertyName1 = 10,
        PropertyName2 = "string data",
        PropertyName3 = new ComplexType()
    };

10

게시물에 더 적합하기 때문에이 게시물 에서 내 대답을 재현 합니다.

C # v7.0부터는 이전 Item1Item2와 같이 사전 정의 된 이름을 기본값으로 사용했던 튜플 특성의 이름을 지정할 수 있습니다.

튜플 리터럴의 속성 이름 지정 :

var myDetails = (MyName: "RBT_Yoga", MyAge: 22, MyFavoriteFood: "Dosa");
Console.WriteLine($"Name - {myDetails.MyName}, Age - {myDetails.MyAge}, Passion - {myDetails.MyFavoriteFood}");

콘솔의 출력 :

이름-RBT_Yoga, 나이-22, 열정-Dosa

메소드에서 Tuple 반환 (명명 된 속성이 있음) :

static void Main(string[] args)
{
    var empInfo = GetEmpInfo();
    Console.WriteLine($"Employee Details: {empInfo.firstName}, {empInfo.lastName}, {empInfo.computerName}, {empInfo.Salary}");
}

static (string firstName, string lastName, string computerName, int Salary) GetEmpInfo()
{
    //This is hardcoded just for the demonstration. Ideally this data might be coming from some DB or web service call
    return ("Rasik", "Bihari", "Rasik-PC", 1000);
}

콘솔의 출력 :

직원 세부 정보 : Rasik, Bihari, Rasik-PC, 1000

명명 된 속성을 가진 튜플 목록 만들기

var tupleList = new List<(int Index, string Name)>
{
    (1, "cow"),
    (5, "chickens"),
    (1, "airplane")
};

foreach (var tuple in tupleList)
    Console.WriteLine($"{tuple.Index} - {tuple.Name}");

콘솔 출력 :

1-소 5-닭 1-비행기

나는 모든 것을 다뤘기를 바랍니다. 내가 놓친 것이 있으면 의견에 의견을 보내주십시오.

참고 : 내 코드 스 니펫은 여기에 자세히 설명 된대로 C # v7의 문자열 보간 기능을 사용하고 있습니다 .


3

MichaelMocko Answered는 훌륭합니다.

하지만 알아 내야 할 몇 가지를 추가하고 싶습니다

(string first, string middle, string last) LookupName(long id)

위의 줄은 .net 프레임 워크 <4.7을 사용하는 경우 컴파일 시간 오류를 발생시킵니다

따라서 .net 프레임 워크 <4.7을 사용하는 프로젝트가 있고 여전히 workTuround보다 ValueTuple을 사용하려는 경우이 nuget 패키지를 설치 합니다



2

아이템 유형이 모두 다르면 여기에 더 직관적으로 만들기 위해 만든 수업이 있습니다.

이 클래스의 사용법 :

var t = TypedTuple.Create("hello", 1, new MyClass());
var s = t.Get<string>();
var i = t.Get<int>();
var c = t.Get<MyClass>();

소스 코드:

public static class TypedTuple
{
    public static TypedTuple<T1> Create<T1>(T1 t1)
    {
        return new TypedTuple<T1>(t1);
    }

    public static TypedTuple<T1, T2> Create<T1, T2>(T1 t1, T2 t2)
    {
        return new TypedTuple<T1, T2>(t1, t2);
    }

    public static TypedTuple<T1, T2, T3> Create<T1, T2, T3>(T1 t1, T2 t2, T3 t3)
    {
        return new TypedTuple<T1, T2, T3>(t1, t2, t3);
    }

    public static TypedTuple<T1, T2, T3, T4> Create<T1, T2, T3, T4>(T1 t1, T2 t2, T3 t3, T4 t4)
    {
        return new TypedTuple<T1, T2, T3, T4>(t1, t2, t3, t4);
    }

    public static TypedTuple<T1, T2, T3, T4, T5> Create<T1, T2, T3, T4, T5>(T1 t1, T2 t2, T3 t3, T4 t4, T5 t5)
    {
        return new TypedTuple<T1, T2, T3, T4, T5>(t1, t2, t3, t4, t5);
    }

    public static TypedTuple<T1, T2, T3, T4, T5, T6> Create<T1, T2, T3, T4, T5, T6>(T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6)
    {
        return new TypedTuple<T1, T2, T3, T4, T5, T6>(t1, t2, t3, t4, t5, t6);
    }

    public static TypedTuple<T1, T2, T3, T4, T5, T6, T7> Create<T1, T2, T3, T4, T5, T6, T7>(T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7)
    {
        return new TypedTuple<T1, T2, T3, T4, T5, T6, T7>(t1, t2, t3, t4, t5, t6, t7);
    }

    public static TypedTuple<T1, T2, T3, T4, T5, T6, T7, T8> Create<T1, T2, T3, T4, T5, T6, T7, T8>(T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7, T8 t8)
    {
        return new TypedTuple<T1, T2, T3, T4, T5, T6, T7, T8>(t1, t2, t3, t4, t5, t6, t7, t8);
    }

}

public class TypedTuple<T>
{
    protected Dictionary<Type, object> items = new Dictionary<Type, object>();

    public TypedTuple(T item1)
    {
        Item1 = item1;
    }

    public TSource Get<TSource>()
    {
        object value;
        if (this.items.TryGetValue(typeof(TSource), out value))
        {
            return (TSource)value;
        }
        else
            return default(TSource);
    }

    private T item1;
    public T Item1 { get { return this.item1; } set { this.item1 = value; this.items[typeof(T)] = value; } }
}

public class TypedTuple<T1, T2> : TypedTuple<T1>
{
    public TypedTuple(T1 item1, T2 item2)
        : base(item1)
    {
        Item2 = item2;
    }

    private T2 item2;
    public T2 Item2 { get { return this.item2; } set { this.item2 = value; this.items[typeof(T2)] = value; } }
}

public class TypedTuple<T1, T2, T3> : TypedTuple<T1, T2>
{
    public TypedTuple(T1 item1, T2 item2, T3 item3)
        : base(item1, item2)
    {
        Item3 = item3;
    }

    private T3 item3;
    public T3 Item3 { get { return this.item3; } set { this.item3 = value; this.items[typeof(T3)] = value; } }
}

public class TypedTuple<T1, T2, T3, T4> : TypedTuple<T1, T2, T3>
{
    public TypedTuple(T1 item1, T2 item2, T3 item3, T4 item4)
        : base(item1, item2, item3)
    {
        Item4 = item4;
    }

    private T4 item4;
    public T4 Item4 { get { return this.item4; } set { this.item4 = value; this.items[typeof(T4)] = value; } }
}

public class TypedTuple<T1, T2, T3, T4, T5> : TypedTuple<T1, T2, T3, T4>
{
    public TypedTuple(T1 item1, T2 item2, T3 item3, T4 item4, T5 item5)
        : base(item1, item2, item3, item4)
    {
        Item5 = item5;
    }

    private T5 item5;
    public T5 Item5 { get { return this.item5; } set { this.item5 = value; this.items[typeof(T5)] = value; } }
}

public class TypedTuple<T1, T2, T3, T4, T5, T6> : TypedTuple<T1, T2, T3, T4, T5>
{
    public TypedTuple(T1 item1, T2 item2, T3 item3, T4 item4, T5 item5, T6 item6)
        : base(item1, item2, item3, item4, item5)
    {
        Item6 = item6;
    }

    private T6 item6;
    public T6 Item6 { get { return this.item6; } set { this.item6 = value; this.items[typeof(T6)] = value; } }
}

public class TypedTuple<T1, T2, T3, T4, T5, T6, T7> : TypedTuple<T1, T2, T3, T4, T5, T6>
{
    public TypedTuple(T1 item1, T2 item2, T3 item3, T4 item4, T5 item5, T6 item6, T7 item7)
        : base(item1, item2, item3, item4, item5, item6)
    {
        Item7 = item7;
    }

    private T7 item7;
    public T7 Item7 { get { return this.item7; } set { this.item7 = value; this.items[typeof(T7)] = value; } }
}

public class TypedTuple<T1, T2, T3, T4, T5, T6, T7, T8> : TypedTuple<T1, T2, T3, T4, T5, T6, T7>
{
    public TypedTuple(T1 item1, T2 item2, T3 item3, T4 item4, T5 item5, T6 item6, T7 item7, T8 item8)
        : base(item1, item2, item3, item4, item5, item6, item7)
    {
        Item8 = item8;
    }

    private T8 item8;
    public T8 Item8 { get { return this.item8; } set { this.item8 = value; this.items[typeof(T8)] = value; } }
}

4
이것은 보상이 거의 없거나 전혀없는 많은 일처럼 보입니다. 그것은 직관적이지 않은 한계 (중복 유형 없음)를 가지고 있으며, 그 유형만으로 값을 검색하는 것은 매우 직관적이지 않으며 실제 사용 사례를 생각할 수 없습니다. 이는 직원에 대한 데이터 테이블을 만든 다음 고유 키가 아닌 이름으로 직원을 검색하기로 결정한 다음 모든 직원에게 다른 이름을 요구하도록하는 것과 같습니다. 이것은 문제에 대한 해결책이 아니며 추가 문제를 일으키는 대가로 솔루션을 사용하고 있습니다.
Flater

그리고 하나님 께서 당신의 영혼에 자비를 베푸시기를 바랍니다.
Jamie M.

1

이것은 매우 성가 시며 향후 버전의 C # 이이 요구를 해결할 것으로 기대합니다. 다른 데이터 구조 유형을 사용하거나 온전함과 코드를 읽는 다른 사람의 온전함을 위해 "항목"의 이름을 바꾸는 것이 가장 쉬운 해결 방법입니다.

Tuple<ApiResource, JSendResponseStatus> result = await SendApiRequest();
ApiResource apiResource = result.Item1;
JSendResponseStatus jSendStatus = result.Item2;

0

클래스를 만들 것이라고 생각하지만 다른 대안은 출력 매개 변수입니다.

public void GetOrderRelatedIds(out int OrderGroupId, out int OrderTypeId, out int OrderSubTypeId, out int OrderRequirementId)

튜플에는 정수만 포함되어 있으므로 튜플은 Dictionary<string,int>

var orderIds = new Dictionary<string, int> {
    {"OrderGroupId", 1},
    {"OrderTypeId", 2},
    {"OrderSubTypeId", 3},
    {"OrderRequirementId", 4}.
};

그러나 나는 그것을 권장하지 않습니다.


0

왜 모두가 인생을 그렇게 힘들게 만들고 있습니까? 튜플은 일시적인 데이터 처리를 위한 것 입니다. Tuples를 항상 사용하면 어느 시점에서 코드를 이해하기가 매우 어려워집니다. 모든 것에 대한 클래스를 만들면 결국 프로젝트가 부풀려 질 수 있습니다.

그러나 균형에 관한 것입니다 ...

문제는 수업을 원하는 것 같습니다. 그리고 완벽을 기하기 위해 아래의이 클래스에는 생성자가 포함되어 있습니다.


이것은 올바른 패턴입니다

  • 맞춤 데이터 유형
    • 더 이상의 기능이 없습니다. Getter 및 Setter는 코드를 사용하여 확장 할 수 있으며 기능 패턴을 실행하면서 이름 패턴이 "_orderGroupId"인 개인 구성원을 가져 오거나 설정할 수 있습니다.
  • 생성자를 포함합니다. 모든 속성이 필수 인 경우 생성자를 하나만 포함하도록 선택할 수도 있습니다 .
  • 모든 생성자를 사용하려면 이와 같은 버블 링이 중복 코드를 피하기위한 적절한 패턴입니다.

public class OrderRelatedIds
{
    public int OrderGroupId { get; set; }
    public int OrderTypeId { get; set; }
    public int OrderSubTypeId { get; set; }
    public int OrderRequirementId { get; set; }

    public OrderRelatedIds()
    {
    }
    public OrderRelatedIds(int orderGroupId)
        : this()
    {
        OrderGroupId = orderGroupId;
    }
    public OrderRelatedIds(int orderGroupId, int orderTypeId)
        : this(orderGroupId)
    {
        OrderTypeId = orderTypeId;
    }
    public OrderRelatedIds(int orderGroupId, int orderTypeId, int orderSubTypeId)
        : this(orderGroupId, orderTypeId)
    {
        OrderSubTypeId = orderSubTypeId;
    }
    public OrderRelatedIds(int orderGroupId, int orderTypeId, int orderSubTypeId, int orderRequirementId)
        : this(orderGroupId, orderTypeId, orderSubTypeId)
    {
        OrderRequirementId = orderRequirementId;
    }
}

또는 정말로 간단하게 원한다면 : 초기화자를 사용할 수도 있습니다.

OrderRelatedIds orders = new OrderRelatedIds
{
    OrderGroupId = 1,
    OrderTypeId = 2,
    OrderSubTypeId = 3,
    OrderRequirementId = 4
};

public class OrderRelatedIds
{
    public int OrderGroupId;
    public int OrderTypeId;
    public int OrderSubTypeId;
    public int OrderRequirementId;
}

0

항목 이름을 요약에 쓸 것입니다. 따라서 helloworld () 함수 위로 마우스를 이동하면 텍스트에 hello = Item1 및 world = Item2라고 표시됩니다.

 helloworld("Hi1,Hi2");

/// <summary>
/// Return hello = Item1 and world Item2
/// </summary>
/// <param name="input">string to split</param>
/// <returns></returns>
private static Tuple<bool, bool> helloworld(string input)
{
    bool hello = false;
    bool world = false;
    foreach (var hw in input.Split(','))
    {
        switch (hw)
        {
            case "Hi1":
                hello= true;
                break;
            case "Hi2":
                world= true;
                break;
        }

    }
    return new Tuple<bool, bool>(hello, world);
}

0

@MichaelMocko 답변에 추가하십시오. 튜플에는 현재 몇 가지 문제가 있습니다.

EF 표현식 트리에서는 사용할 수 없습니다

예:

public static (string name, string surname) GetPersonName(this PersonContext ctx, int id)
{
    return ctx.Persons
        .Where(person => person.Id == id)
        // Selecting as Tuple
        .Select(person => (person.Name, person.Surname))
        .First();
}

"표현식 트리에는 튜플 리터럴이 포함되어 있지 않을 수 있습니다"라는 오류가 발생하여 컴파일되지 않습니다. 불행히도, 표현식 트리 API는 언어에 추가 될 때 튜플을 지원하여 확장되지 않았습니다.

https://github.com/dotnet/roslyn/issues/12897 업데이트에 대한이 문제를 추적 (투표) 하십시오.

이 문제를 해결하려면 먼저 익명 유형으로 캐스트 한 다음 값을 튜플로 변환하십시오.

// Will work
public static (string name, string surname) GetPersonName(this PersonContext ctx, int id)
{
    return ctx.Persons
        .Where(person => person.Id == id)
        .Select(person => new { person.Name, person.Surname })
        .ToList()
        .Select(person => (person.Name, person.Surname))
        .First();
}

또 다른 옵션은 ValueTuple을 사용하는 것입니다.

// Will work
public static (string name, string surname) GetPersonName(this PersonContext ctx, int id)
{
    return ctx.Persons
        .Where(person => person.Id == id)
        .Select(person => ValueTuple.Create(person.Name, person.Surname))
        .First();
}

참고 문헌 :

람다에서 해체 할 수 없습니다

지원을 추가 할 제안이 있습니다 : https://github.com/dotnet/csharplang/issues/258

예:

public static IQueryable<(string name, string surname)> GetPersonName(this PersonContext ctx, int id)
{
    return ctx.Persons
        .Where(person => person.Id == id)
        .Select(person => ValueTuple.Create(person.Name, person.Surname));
}

// This won't work
ctx.GetPersonName(id).Select((name, surname) => { return name + surname; })

// But this will
ctx.GetPersonName(id).Select(t => { return t.name + t.surname; })

참고 문헌 :

그들은 잘 직렬화되지 않습니다

using System;
using Newtonsoft.Json;

public class Program
{
    public static void Main() {
        var me = (age: 21, favoriteFood: "Custard");
        string json = JsonConvert.SerializeObject(me);

        // Will output {"Item1":21,"Item2":"Custard"}
        Console.WriteLine(json); 
    }
}

튜플 필드 이름은 컴파일 타임에만 사용할 수 있으며 런타임에 완전히 지워집니다.

참고 문헌 :


-1

Tuple이 포함 된 클래스를 작성할 수 있습니다.

Equals 및 GetHashCode 함수를 대체해야합니다.

== 및! = 연산자.

class Program
{
    public class MyTuple
    {
        private Tuple<int, int> t;

        public MyTuple(int a, int b)
        {
            t = new Tuple<int, int>(a, b);
        }

        public int A
        {
            get
            {
                return t.Item1;
            }
        }

        public int B
        {
            get
            {
                return t.Item2;
            }
        }

        public override bool Equals(object obj)
        {
            return t.Equals(((MyTuple)obj).t);
        }

        public override int GetHashCode()
        {
            return t.GetHashCode();
        }

        public static bool operator ==(MyTuple m1, MyTuple m2)
        {
            return m1.Equals(m2);
        }

        public static bool operator !=(MyTuple m1, MyTuple m2)
        {
            return !m1.Equals(m2);
        }
    }

    static void Main(string[] args)
    {
        var v1 = new MyTuple(1, 2);
        var v2 = new MyTuple(1, 2);

        Console.WriteLine(v1 == v2);

        Dictionary<MyTuple, int> d = new Dictionary<MyTuple, int>();
        d.Add(v1, 1);

        Console.WriteLine(d.ContainsKey(v2));
    }
}

돌아올 것이다 :

진실

진실


2
이 데이터 유형에 대한 클래스를 이미 구현 한 경우 왜 속성이 아닌 기본 데이터에 대해 Tuple을 선언합니까?
bytecode77

Equals 함수의 값으로 제공되는 튜플 속성을 사용하고 싶습니다
ss

보너스 일 수도 있습니다. 그러나 다른 한편으로는 기본적으로 Item1에서 ItemX에 이르는 속성을 가진 클래스를 만들었습니다. 튜플을 사용하는 대신 Equals ()에서 적절한 이름과 더 많은 코드를 선택합니다.
bytecode77

-1

C # 7 튜플 예

var tuple = TupleExample(key, value);

     private (string key1, long value1) ValidateAPIKeyOwnerId(string key, string value)
            {
                return (key, value);
            }
      if (!string.IsNullOrEmpty(tuple.key1) && tuple.value1 > 0)
          {
                    //your code

                }     
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.