Choisir entre les types anonymes et tuples

Choisissez entre les types anonymes, System.Tuple<T1,T2>et System.ValueTuple<T1,T2> en fonction de la facilité d’utilisation, des performances et des compromis. ValueTuple fournit un type valeur avec la flexibilité des types anonymes. Cet article explique quand choisir chaque type.

Facilité d’utilisation et fonctionnalités

Avec Language-Integrated Requête (LINQ), vous projetez souvent des résultats de requête en types anonymes qui contiennent quelques propriétés sélectionnées à partir des objets sources. L’exemple suivant instancie un tableau d’objets DateTime et projete chaque objet dans un type anonyme avec deux propriétés.

var dates = new[]
{
    DateTime.UtcNow.AddHours(-1),
    DateTime.UtcNow,
    DateTime.UtcNow.AddHours(1),
};

foreach (var anonymous in
             dates.Select(
                 date => new { Formatted = $"{date:MMM dd, yyyy hh:mm zzz}", date.Ticks }))
{
    Console.WriteLine($"Ticks: {anonymous.Ticks}, formatted: {anonymous.Formatted}");
}

Les types anonymes sont instanciés à l’aide de l’opérateur new , et les noms et types de propriétés sont déduits de la déclaration. Si deux initialiseurs d’objets anonymes ou plus dans le même assembly spécifient une séquence de propriétés qui se trouvent dans le même ordre et qui ont les mêmes noms et types, le compilateur traite les objets comme des instances du même type. Elles partagent les mêmes informations de type générées par le compilateur.

L’extrait de code C# précédent projette un type anonyme avec deux propriétés, comme la classe C# générée par le compilateur suivante :

internal sealed class f__AnonymousType0
{
    public string Formatted { get; }
    public long Ticks { get; }

    public f__AnonymousType0(string formatted, long ticks)
    {
        Formatted = formatted;
        Ticks = ticks;
    }
}

Pour plus d’informations, consultez les types anonymes. La même fonctionnalité existe avec les tuples lors de la projection dans des requêtes LINQ. Vous pouvez sélectionner des propriétés dans des tuples. Ces tuples circulent dans la requête, comme le feraient les types anonymes. Considérez maintenant l’exemple suivant à l’aide du System.Tuple<string, long>.

var dates = new[]
{
    DateTime.UtcNow.AddHours(-1),
    DateTime.UtcNow,
    DateTime.UtcNow.AddHours(1),
};

foreach (var tuple in
            dates.Select(
                date => new Tuple<string, long>($"{date:MMM dd, yyyy hh:mm zzz}", date.Ticks)))
{
    Console.WriteLine($"Ticks: {tuple.Item2}, formatted: {tuple.Item1}");
}

Avec l’instance System.Tuple<T1,T2>, l’instance expose les propriétés d’élément numérotées, telles que Item1, et Item2. Ces noms de propriétés peuvent rendre plus difficile la compréhension de l’intention des valeurs de propriété, car le nom de propriété fournit uniquement l’ordinal. En outre, les types System.Tuple sont des types class de référence. System.ValueTuple<T1,T2>, toutefois, est un type struct valeur. L’extrait de code C# suivant, utilise ValueTuple<string, long> pour la projection. Ce faisant, il assigne à l’aide d’une syntaxe littérale.

var dates = new[]
{
    DateTime.UtcNow.AddHours(-1),
    DateTime.UtcNow,
    DateTime.UtcNow.AddHours(1),
};

foreach (var (formatted, ticks) in
            dates.Select(
                date => (Formatted: $"{date:MMM dd, yyyy at hh:mm zzz}", date.Ticks)))
{
    Console.WriteLine($"Ticks: {ticks}, formatted: {formatted}");
}

Pour plus d’informations sur les tuples, consultez Tuple types (référence C#) ou Tuples (Visual Basic).

Les exemples précédents sont tous équivalents fonctionnellement, mais il existe de légères différences dans leur facilité d’utilisation et leurs implémentations sous-jacentes.

Compromis

Vous voudrez peut-être toujours utiliser ValueTuple au lieu de Tuple pour les types anonymes, mais il existe des compromis que vous devez prendre en compte. Les ValueTuple types sont mutables, tandis que Tuple sont en lecture seule. Les types anonymes peuvent être utilisés dans les arborescences d’expressions, tandis que les tuples ne peuvent pas. Le tableau suivant présente quelques-unes des principales différences.

Principales différences

Nom Modificateur d’accès Type Nom de membre personnalisé Prise en charge de la déconstruction Prise en charge de l’arborescence de l’expression
Types anonymes internal class ✔️ ✔️
Tuple public class ✔️
ValueTuple public struct ✔️ ✔️

Sérialisation

L’un des points importants à prendre en compte lors du choix d’un type est de savoir s’il doit être sérialisé ou non. La sérialisation est le processus de conversion de l’état d’un objet en un formulaire qui peut être conservé ou transporté. Pour plus d’informations, consultez sérialisation. Lorsque la sérialisation est importante, la création d’un class ou d’un struct est préférée aux types anonymes ou aux types de tuple.

Performances

Les performances entre ces types dépendent du scénario. L’impact majeur implique le compromis entre les allocations et la copie. Dans la plupart des scénarios, l’impact est petit. Lorsque des impacts majeurs peuvent survenir, des mesures doivent être prises pour informer la décision.

Conclusion

Lorsqu'un développeur choisit entre les tuples et les types anonymes, plusieurs facteurs sont à considérer. En règle générale, si vous ne travaillez pas avec des arborescences d’expressions, et que vous êtes à l’aise avec la syntaxe tuple, choisissez ValueTuple comme ils fournissent un type valeur avec la flexibilité nécessaire pour nommer les propriétés. Si vous utilisez des arborescences d’expressions et que vous préférez nommer des propriétés, choisissez des types anonymes. Sinon, utilisez Tuple.

Voir aussi