web-dev-qa-db-fra.com

Alternative non en lecture seule aux types anonymes

En C #, un type anonyme peut être comme suit:

method doStuff(){
     var myVar = new {
         a = false, 
         b = true
     }

     if (myVar.a) 
     {
         // Do stuff             
     }
}

Cependant, les éléments suivants ne seront pas compilés:

method doStuff(){
     var myVar = new {
         a = false, 
         b = true
     }

     if (myVar.a) 
     {
         myVar.b = true;
     }
}

En effet, les champs de myVar sont en lecture seule et ne peuvent pas être affectés. Il semble vouloir faire quelque chose comme ce dernier est assez courant; peut-être que la meilleure solution que j'ai vue est de simplement définir une structure en dehors de la méthode.

Cependant, n'y a-t-il vraiment pas d'autre moyen de faire fonctionner le bloc ci-dessus? La raison pour laquelle cela me dérange est que myVar est une variable locale de ce champ, il semble donc qu'elle ne devrait être référencée qu'à l'intérieur de la méthode qui l'utilise. De plus, avoir besoin de placer la structure en dehors de la méthode peut rendre la déclaration d'un objet assez éloignée de son utilisation, surtout dans une méthode longue.

En d'autres termes, existe-t-il une alternative aux types anonymes qui me permettra de définir un "struct" comme celui-ci (je me rends compte que le struct existe en C # et doit être défini en dehors d'une méthode) sans le rendre en lecture seule? Si non, y a-t-il quelque chose de fondamentalement faux à vouloir faire cela et devrais-je utiliser une approche différente?

34
Superbest

Non, vous devrez créer votre propre classe ou structure pour ce faire (de préférence une classe si vous voulez qu'elle soit mutable - les structures mutables sont horribles).

Si vous ne vous souciez pas des implémentations de Equals/ToString/GetHashCode, c'est assez simple:

public class MyClass {
    public bool Foo { get; set; }
    public bool Bar { get; set; }
}

(J'utiliserais toujours des propriétés plutôt que des champs, pour diverses raisons .)

Personnellement, je me retrouve généralement à vouloir un type immuable que je peux passer entre les méthodes, etc. - Je veux une version nommée de la fonctionnalité de type anonyme existante ...

34
Jon Skeet

Existe-t-il une alternative aux types anonymes qui me permettra de définir de manière concise un simple type "enregistrement" comme celui-ci sans le rendre en lecture seule?

Non. Vous devrez créer un type nominal.

Si non, y a-t-il quelque chose de fondamentalement mal à vouloir faire cela?

Non, c'est une fonctionnalité raisonnable que nous avons déjà envisagée.

Je note que dans Visual Basic, les types anonymes sont mutables si vous voulez qu'ils le soient.

La seule chose qui est vraiment "fondamentalement erronée" à propos d'un type anonyme mutable est qu'il serait dangereux d'en utiliser un comme clé de hachage. Nous avons conçu des types anonymes avec l'hypothèse que (1) vous allez les utiliser comme clés dans les équidés dans les compréhensions de requêtes LINQ, et (2) dans LINQ-to-Objects et autres implémentations, les jointures seront implémentées à l'aide de tables de hachage. Par conséquent, les types anonymes devraient être utiles comme clés de hachage, et les clés de hachage mutables sont dangereuses.

Dans Visual Basic, l'implémentation GetHashCode ne consomme aucune information des champs mutables de types anonymes. Bien que ce soit un compromis raisonnable, nous avons simplement décidé qu'en C #, la complexité supplémentaire n'en valait pas la peine.

26
Eric Lippert

En C # 7, nous pouvons tirer parti de tuples nommés pour faire l'affaire:

(bool a, bool b) myVar = (false, true);

if (myVar.a)
{
    myVar.b = true;
}
6
Kees C. Bakker

Vous ne pourrez pas obtenir la syntaxe d'initialisation de Nice mais la classe ExpandoObject introduite dans .NET 4 serait une solution viable.

dynamic eo = new ExpandoObject();

eo.SomeIntValue = 5;
eo.SomeIntValue = 10; // works fine
3
AnthonyOSX

Pour les types d'opérations ci-dessus, vous devez définir votre propre mutable STRUCT. Les structures mutables peuvent poser un problème aux rédacteurs de compilateurs comme Eric Lippert, et il existe des limitations malheureuses dans la manière dont .net les gère, mais néanmoins la sémantique des structures "Plain Old Data" mutables (structures dans lesquelles tous les champs sont publics, et les seuls les fonctions publiques qui écrivent this sont des constructeurs, ou sont appelées exclusivement à partir de constructeurs) offrent une sémantique bien plus claire que celle qui peut être obtenue via les classes.

Par exemple, considérez ce qui suit:

 struct Foo {
 public int bar; 
 ... autres choses; 
} 
 int test (Action <Foo []> proc1, Action <Foo> proc2) 
 {
 foo myFoos [] = new Foo [100]; 
 proc1 (myFoos); 
 myFoos [4] .bar = 9; 
 proc2 (myFoos [4]); // Pass-by-value 
 Return myFoos [4] .bar; 
} 

En supposant qu'il n'y a pas de code non sécurisé et que les délégués transmis peuvent être appelés et retourneront dans un temps fini, que retournera test()? Le fait que Foo soit une structure avec un champ public bar suffit pour répondre à la question: il retournera 9, indépendamment de ce qui apparaît dans la déclaration de Foo, et quelles que soient les fonctions passées dans proc1 et proc2. Si Foo était une classe, il faudrait examiner chaque Action<Foo[]> Et Action<Foo> Qui existe, ou qui existera jamais, pour savoir ce que test() ferait revenir. Déterminer que Foo est une structure avec un champ public bar semble beaucoup plus facile que d'examiner toutes les fonctions passées et futures qui pourraient être transmises.

Les méthodes structur qui modifient this sont particulièrement mal gérées dans .net, donc si l'on a besoin d'utiliser une méthode pour modifier une structure, il est presque certainement préférable d'utiliser l'un de ces modèles:

 myStruct = monStruct.ModifiedInSomeFashion (...); // Approche # 1 
 MyStructType.ModifyInSomeFashion (réf myStruct, ...); // Approche # 2 

que le motif:

 myStruct.ModifyInSomeFashion (...); 

À condition que l'on utilise l'approche ci-dessus pour modifier les structures, cependant, les structures mutables ont l'avantage d'autoriser du code qui est à la fois plus efficace et plus facile à lire que les structures immuables ou les classes immuables, et est beaucoup moins problématique que les classes mutables. Pour les choses qui représentent une agrégation de valeurs, sans identité en dehors des valeurs qu'elles contiennent, les types de classes mutables sont souvent la pire représentation possible.

2
supercat

Je trouve vraiment ennuyeux que vous ne puissiez pas définir des propriétés anonymes en lecture/écriture comme vous le pouvez dans VB - souvent je veux renvoyer des données à partir d'une base de données à l'aide de la projection EF/LINQ, puis faire certains massages des données en c # qui ne peuvent pas être effectués dans la base de données pour quelque raison que ce soit. Le moyen le plus simple de le faire est d'itérer sur les instances anonymes existantes et de mettre à jour les propriétés au fur et à mesure. REMARQUE ce n'est pas si mal maintenant dans EF. Core, car vous pouvez enfin mélanger les fonctions db et les fonctions .net dans une seule requête.

Ma solution de contournement est d'utiliser la réflexion et sera désapprouvée et votée à la baisse, mais fonctionne; l'acheteur se méfie si l'implémentation sous-jacente change et que tout votre code se casse.

public static class AnonClassHelper {

    public static void SetField<T>(object anonClass, string fieldName, T value) {
        var field = anonClass.GetType().GetField($"<{fieldName}>i__Field", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);

        field.SetValue(anonClass, value);
    }

}
// usage
AnonClassHelper.SetField(inst, nameof(inst.SomeField), newVal);

Une alternative que j'ai utilisée pour traiter des chaînes est de créer des propriétés de type StringBuilder, puis ces propriétés individuelles seront configurables via les méthodes StringBuilder une fois que vous aurez une instance de votre type anonyme.

0
LMK