166 votes

Est à l’aide de Tuples dans mon .NET 4.0 Code une mauvaise décision de conception ?

Avec l'addition du n-uplet de la classe .net 4, j'ai essayé de décider si l'aide dans ma conception est un mauvais choix ou pas. La façon dont je le vois, un n-uplet peut être un raccourci pour l'écriture d'un résultat de la classe (je suis sûr qu'il ya d'autres utilisations).

Donc ceci:

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

Est équivalent à ceci:

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

Afin de mettre de côté la possibilité que j'ai manque le point de n-Uplets, est l'exemple avec un n-uplet d' un mauvais choix de conception? Pour moi, il semble que moins d'encombrement, mais pas en tant que auto-documenter et propre. Ce qui signifie qu'avec le type ResultType, il est très clair plus tard ce que chaque partie de la classe des moyens, mais vous avez un code supplémentaire pour maintenir. Avec l' Tuple<string, int> vous aurez besoin de regarder et de comprendre ce que chaque Item représente, mais vous écrire et maintenir des moins de code.

Toute l'expérience que vous avez eu avec ce choix serait grandement apprécié.

162voto

Bryan Watts Points 22810

Les Tuples sont grands si vous contrôler à la fois la création et l'utilisation d'eux - vous pouvez maintenir le contexte, ce qui est essentiel à la compréhension.

Sur une API publique, cependant, ils sont moins efficaces. Le consommateur (pas vous) doit deviner ou de chercher de la documentation, en particulier pour des choses comme l' Tuple<int, int>.

Je les utiliserais pour private/membres internes, mais l'utilisation de résultat classes pour le public et les membres protégés.

Cette réponse a également quelques infos.

79voto

LBushkin Points 60611

La façon dont je le vois, un Tuple est un raccourci pour l'écriture d'un résultat de la classe (je suis sûr qu'il ya d'autres utilisations).

Il existe en effet d'autres utilisations pour Tuple<> - la plupart d'entre eux font abstraction de la sémantique d'un groupe de types qui partagent une structure similaire, et de les traiter simplement comme ensemble ordonné de valeurs. Dans tous les cas, une indemnité de n-uplets, c'est qu'ils évitez d'encombrer votre espace de noms avec des données, seules les classes qui exposer des propriétés, mais pas les méthodes.

Voici un exemple d'une utilisation raisonnable pour Tuple<>:

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

Dans l'exemple ci-dessus, nous voulons représenter une paire d'adversaires, un tuple est une façon pratique de l'appariement de ces instances sans avoir à créer une nouvelle classe. Voici un autre exemple:

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

Une main de poker peut être considéré simplement comme un ensemble de cartes - et n-uplet (peut être) un moyen raisonnable d'exprimer ce concept.

mettant de côté la possibilité que je me manque le point de n-Uplets, est l' exemple avec un n-uplet d'une mauvaise conception choix?

De retour fortement typé Tuple<> cas dans le cadre d'une API publique pour un type de public est rarement une bonne idée. Comme vous le reconnaître, les tuples, les parties impliquées (bibliothèque de l'auteur, à la bibliothèque de l'utilisateur) afin de convenir à l'avance de l'heure sur l'usage et l'interprétation du n-uplet types utilisés. Il est assez difficile de créer des Api qui sont intuitives et claires, à l'aide de Tuple<> public ne peut que brouiller l'intention et le comportement de l'API.

Les types anonymes sont aussi une forme de tuple - cependant, ils sont fortement typés et vous permettent de spécifier claire, informative des noms pour les propriétés appartenant à ce type. Mais les types anonymes sont difficiles à utiliser sur les différentes méthodes qu'ils ont été principalement ajoutée pour soutenir les technologies comme LINQ où les projections serait de produire des types à qui nous n'aurions pas envie de leur attribuer un nom. (Oui, je sais que les types anonymes avec les mêmes types et propriétés nommées sont consolidés par le compilateur).

Ma règle d'or est: si vous retournez à partir de votre interface publique - faire d'un type nommé.

Mon autre règle de base pour l'utilisation de n-uplets est: nom des arguments de méthode et localc des variables de type Tuple<> aussi clairement que possible - prendre le nom de représenter le sens des relations entre les éléments du tuple. Pense à ma var opponents = ... exemple.

Voici un exemple de cas réels où je l'ai utilisé Tuple<> pour éviter de déclarer des données de type uniquement pour une utilisation uniquement à l'intérieur de mon propre assemblée. La situation implique le fait que lors de l'utilisation de générique dictionnaires contenant des types anonymes, il devient difficile d'utiliser l' TryGetValue() méthode pour rechercher des éléments dans le dictionnaire, parce que la méthode nécessite une out paramètre qui ne peut pas être nommé:

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

P. S. Il y a une autre (plus simple) moyen de contourner les problèmes liés à des types anonymes dans les dictionnaires, et qui consiste à utiliser l' var mot-clé pour permettre au compilateur de "déduire" le type pour vous. Voici la version:

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

18voto

Matthew Whited Points 12255

Tuples peut être utile... mais ils peuvent aussi être une douleur plus tard. Si vous avez une méthode qui retourne Comment savez-vous ce que ces valeurs sont ensuites. Ils étaient ou ils étaient `` .

9voto

Daniel Earwicker Points 63298

Les Tuples sont assez décevante, outre le CLR du point de vue d'un programmeur C#. Si vous avez une collection d'éléments qui varie dans la longueur, vous n'avez pas besoin d'avoir statique unique noms au moment de la compilation.

Mais si vous avez une collection de longueur constante, ce qui implique que le fixe d'endroits dans la collection comportent chacun un pré-définis. Et il est toujours préférable de leur donner statiques appropriées noms dans ce cas, plutôt que d'avoir à se souvenir de la signification de l' Item1, Item2, etc.

Anonyme classes en C# fournissent déjà une superbe solution pour la plupart des communes de l'usage privé de tuples, et ils donnent des noms significatifs pour les éléments, de sorte qu'ils sont en fait de qualité supérieure en ce sens. Le seul problème est qu'ils ne peuvent pas s'échapper de méthodes appelées. Je préfère voir cette restriction levée (peut-être que pour les méthodes privées) que d'avoir un soutien spécifique pour les tuples en 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();
    }
}

8voto

johnny g Points 2472

Similaire au mot clé `` , il est conçu comme une commodité - mais est aussi facilement abusé.

À mon avis plus humbles, n’exposez pas `` comme une classe de retour. Utilisez-le en privé, si un service ou une structure de données du composant l’exige, mais bien formé des classes bien connus de retour de méthodes publiques.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X