[C#] C # 코드에서 .NET 4.0 튜플을 사용하는 것이 좋지 않은 디자인 결정입니까?

튜플을 추가하면.net 4에 클래스 디자인에 사용하는 것이 나쁜 선택인지 여부를 결정하려고했습니다. 내가 보는 방식으로 Tuple 은 결과 클래스를 작성하는 지름길이 될 수 있습니다 (다른 용도도 있음).

그래서 이거:

public class ResultType
{
    public string StringValue { get; set; }
    public int IntValue { get; set; }
}

public ResultType GetAClassedValue()
{
    //..Do Some Stuff
    ResultType result = new ResultType { StringValue = "A String", IntValue = 2 };
    return result;
}

이것과 동일합니다 :

public Tuple<string, int> GetATupledValue()
{
    //...Do Some stuff
    Tuple<string, int> result = new Tuple<string, int>("A String", 2);
    return result;
}

옆으로 난 튜플의 지점을 잃었 가능성을 설정하며,이와 예입니다 튜플은 나쁜 디자인 선택? 나에게 그것은 덜 어수선 해 보이지만 자기 문서화와 깨끗함은 아닙니다. 유형과 함께 의미ResultType 을 사용하면 클래스의 각 부분이 무엇을 의미하는지 명확하게 알 수 있지만 유지 관리해야 할 추가 코드가 있습니다. 를 사용하면 Tuple<string, int>각각이 Item나타내는 내용을 찾아서 파악해야 하지만 코드를 적게 작성하고 유지 관리해야합니다.

이 선택에 대한 경험은 대단히 감사하겠습니다.



답변

튜플은 생성 및 사용을 모두 제어 할 경우 유용합니다. 컨텍스트를 유지 관리 할 수 ​​있으므로 이해해야합니다.

그러나 공용 API에서는 효과가 떨어집니다. 소비자 (귀하가 아닌)는 문서를 추측하거나 찾아야합니다. 특히Tuple<int, int> .

개인 / 내부 회원에게는 사용하지만 공개 / 보호 회원에게는 결과 클래스를 사용합니다.

이 답변 에는 정보가 있습니다.


답변

내가 보는 방식으로 Tuple은 결과 클래스를 작성하는 지름길입니다 (다른 용도도 있음).

실제로 다른 귀중한 용도가 있습니다 Tuple<> . 대부분 유사한 구조를 공유하는 특정 유형의 그룹의 의미를 추상화하고 단순히 일련의 값으로 취급합니다. 모든 경우에 튜플의 이점은 속성은 노출하지만 메서드는 노출하지 않는 데이터 전용 클래스로 네임 스페이스를 어지럽히 지 않는다는 것입니다.

다음은 합리적으로 사용되는 예입니다 Tuple<>.

var opponents = new Tuple<Player,Player>( playerBob, playerSam );

위 예제에서 우리는 한 쌍의 상대를 나타내려고합니다. 튜플은 새 클래스를 만들지 않고도 이러한 인스턴스를 연결하는 편리한 방법입니다. 또 다른 예는 다음과 같습니다.

var pokerHand = Tuple.Create( card1, card2, card3, card4, card5 );

포커 패는 단순한 카드 세트로 생각할 수 있으며 튜플은 그 개념을 표현하는 합리적인 방법입니다.

Tuples의 요점을 놓칠 가능성을 제쳐두고 Tuple이있는 예는 나쁜 디자인 선택입니까?

Tuple<>퍼블릭 타입의 퍼블릭 API의 일부로 강력한 타입의 인스턴스를 반환하는 것은 좋은 생각이 아닙니다. 본인이 알고 있듯이 튜플을 사용하려면 관련 당사자 (라이브러리 작성자, 라이브러리 사용자)가 사용중인 튜플 유형의 목적과 해석에 대해 미리 동의해야합니다. 직관적이고 명확한 API를 사용하여Tuple<>공개적으로 API의 의도와 행동을 모호하게하는 것만으로 입니다.

익명 형식도 일종의 튜플 이지만 강력하게 형식이 지정되어 있으며 해당 형식에 속하는 속성에 대해 명확하고 유익한 이름을 지정할 수 있습니다. 그러나 익명 유형은 다른 방법으로 사용하기가 어렵습니다. 주로 LINQ와 같은 기술을 지원하기 위해 추가되었습니다. (예, 동일한 유형과 명명 된 속성을 가진 익명 유형이 컴파일러에 의해 통합되어 있음을 알고 있습니다).

내 경험 법칙은 공용 인터페이스에서 반환하는 경우 명명 된 유형으로 만드십시오 .

튜플을 사용하는 또 다른 경험의 규칙은 다음 같습니다. 이름 메서드 인수와 Tuple<>가능한 유형의 localc 변수는 가능한 명확하게-이름이 튜플 요소 간의 관계의 의미를 나타내도록합니다. 내 var opponents = ...예를 생각해보십시오 .

다음 은 내 어셈블리 내에서만 사용 Tuple<>하기 위해 데이터 전용 유형 선언하지 않기 위해 사용한 실제 사례의 예입니다 . 익명 유형을 포함하는 일반 사전을 사용하는 경우 TryGetValue()메소드에 out이름을 지정할 수없는 매개 변수가 필요하기 때문에 메소드를 사용 하여 사전에서 항목을 찾는 것이 어려워지는 상황이 있습니다 .

public static class DictionaryExt
{
    // helper method that allows compiler to provide type inference
    // when attempting to locate optionally existent items in a dictionary
    public static Tuple<TValue,bool> Find<TKey,TValue>(
        this IDictionary<TKey,TValue> dict, TKey keyToFind )
    {
        TValue foundValue = default(TValue);
        bool wasFound = dict.TryGetValue( keyToFind, out foundValue );
        return Tuple.Create( foundValue, wasFound );
    }
}

public class Program
{
    public static void Main()
    {
        var people = new[] { new { LastName = "Smith", FirstName = "Joe" },
                             new { LastName = "Sanders", FirstName = "Bob" } };

        var peopleDict = people.ToDictionary( d => d.LastName );

        // ??? foundItem <= what type would you put here?
        // peopleDict.TryGetValue( "Smith", out ??? );

        // so instead, we use our Find() extension:
        var result = peopleDict.Find( "Smith" );
        if( result.First )
        {
            Console.WriteLine( result.Second );
        }
    }
}

PS 사전에서 익명 유형으로 인해 발생하는 문제를 해결하는 또 다른 (간단한) 방법이 있습니다. 즉, var키워드를 사용 하여 컴파일러가 유형을 ‘유추’할 수 있습니다. 그 버전은 다음과 같습니다.

var foundItem = peopleDict.FirstOrDefault().Value;
if( peopleDict.TryGetValue( "Smith", out foundItem ) )
{
   // use foundItem...
}


답변

튜플은 유용 할 수 있지만 나중에 고통이 될 수도 있습니다. 반환 Tuple<int,string,string,int>하는 메서드가 있다면 나중에 그 값이 무엇인지 어떻게 알 수 있습니까? 그들이 ID, FirstName, LastName, Age거나 그들 UnitNumber, Street, City, ZipCode이었다.


답변

튜플은 C # 프로그래머의 관점에서 CLR에 상당히 압도적입니다. 길이가 다양한 항목 모음이 있으면 컴파일시 고유 한 정적 이름을 가질 필요가 없습니다.

그러나 길이가 일정한 컬렉션이있는 경우 컬렉션의 고정 위치마다 각각 미리 정의 된 특정 의미가 있음을 의미합니다. 그리고는 항상 다소의 중요성을 기억하는 것보다, 그 경우에 그들에게 적절한 정적 이름을 지정하는 것이 좋습니다 Item1, Item2

C #의 익명 클래스는 이미 가장 일반적으로 사용되는 튜플 사용에 대한 최상의 솔루션을 제공하며 항목에 의미있는 이름을 지정하므로 실제로 그 점에서 우수합니다. 유일한 문제는 명명 된 메소드에서 유출 될 수 없다는 것입니다. C #에서 튜플을 구체적으로 지원하는 것보다 제한이 해제 된 것 같습니다 (아마도 개인 메서드에만 해당).

private var GetDesserts()
{
    return _icecreams.Select(
        i => new { icecream = i, topping = new Topping(i) }
    );
}

public void Eat()
{
    foreach (var dessert in GetDesserts())
    {
        dessert.icecream.AddTopping(dessert.topping);
        dessert.Eat();
    }
}


답변

keyword var와 유사하게 , 편의를위한 것이지만 쉽게 남용됩니다.

가장 겸손한 견해 Tuple로는, 귀국 수업으로 노출하지 마십시오 . 서비스 또는 구성 요소의 데이터 구조에 필요한 경우 비공개로 사용하지만 공용 메서드에서 잘 구성된 잘 알려진 클래스를 반환하십시오.

// one possible use of tuple within a private context. would never
// return an opaque non-descript instance as a result, but useful
// when scope is known [ie private] and implementation intimacy is
// expected
public class WorkflowHost
{
    // a map of uri's to a workflow service definition 
    // and workflow service instance. By convention, first
    // element of tuple is definition, second element is
    // instance
    private Dictionary<Uri, Tuple<WorkflowService, WorkflowServiceHost>> _map =
        new Dictionary<Uri, Tuple<WorkflowService, WorkflowServiceHost>> ();
}


답변

같은 클래스를 사용하는 ResultType것이 더 명확합니다. (튜플로가 호출 될 것입니다 반면에 당신은 클래스의 필드에 의미있는 이름을 부여 할 수 Item1Item2). 두 필드의 유형이 동일한 경우 훨씬 더 중요합니다. 이름이 명확하게 구분됩니다.


답변

장식-정렬-장식하지 않은 패턴으로 튜플을 사용하는 것은 어떻습니까? (Perl 사람들을위한 Schwartzian 변환). 여기에 확실한 예가 있지만, Tuples는 이런 종류의 일을 처리하는 좋은 방법 인 것 같습니다.

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            string[] files = Directory.GetFiles("C:\\Windows")
                    .Select(x => new Tuple<string, string>(x, FirstLine(x)))
                    .OrderBy(x => x.Item2)
                    .Select(x => x.Item1).ToArray();
        }
        static string FirstLine(string path)
        {
            using (TextReader tr = new StreamReader(
                        File.Open(path, FileMode.Open)))
            {
                return tr.ReadLine();
            }
        }
    }
}

이제 두 요소의 Object [] 또는이 특정 예제에서 두 요소의 문자열 []을 사용할 수있었습니다. 요점은 내부적으로 사용되며 읽기 쉬운 튜플의 두 번째 요소로 무엇이든 사용할 수 있다는 것입니다.