Question

Considérez ceci:

var me = new { FirstName = "John", LastName = "Smith" };

Cela convient car nous pouvons alors le faire:

Console.WriteLine("{0} {1}", me.FirstName, me.LastName);

Cependant, nous ne pouvons pas faire cela:

public T GetMe()
{
    return new { FirstName = "John", LastName = "Smith" };
}

parce que nous ne connaissons pas le type de T.

Nous pourrions faire ceci:

public object GetMe()
{
    return new { FirstName = "John", LastName = "Smith" };
}

mais il faudrait alors inspecter les propriétés de l'objet en utilisant la réflexion pour y accéder:

var p = new Prog();
object o = p.GetMe();
Type t = o.GetType();
foreach (var prop in t.GetProperties())
{
    Console.WriteLine(prop.Name + ": " + prop.GetValue(o, null));
}

Cependant, si nous pouvions nommer un type anonyme tel que nous le définissons? Bien sûr, il ne serait plus anonyme, mais ce serait plus succinct et facile à gérer qu'une définition de classe normale.

Considérez ceci:

public Person GetMe()
{
    return new public class Person { FirstName = "John", LastName = "Smith" };
}

L'avantage étant qu'il serait alors possible de renvoyer le résultat d'une requête Linq compliquée à partir d'une méthode sans avoir à définir explicitement la classe.

Considérez cette requête Linq relativement complexe:

List<int> list = new List<int>();
var query = from number in list
            select
                new
                    {
                        Number = number,
                        Square = number*number,
                        Absolute = Math.Abs(number),
                        Range = Enumerable.Range(0, number)
                    };

Au lieu de définir une classe comme suit:

public class MyNumbers
{
    public int Number { get; set; }
    public int Square { get; set; }
    public int Absolute { get; set; }
    public IEnumerable<int> Range { get; set; }
}

afin de renvoyer la variable de requête d'une méthode, nous pourrions plutôt simplement faire ceci:

List<int> list = new List<int>();
return from number in list
            select new public class MyNumbers
                    {
                        Number = number,
                        Square = number*number,
                        Absolute = Math.Abs(number),
                        Range = Enumerable.Range(0, number)
                    };
Était-ce utile?

La solution

En fait, il existe un "hack". que vous pouvez faire pour récupérer un type anonyme à partir d’une méthode. Considérez ceci:

public object MyMethod()
    {
        var myNewObject = new
        {
            stringProperty = "Hello, World!",
            intProperty = 1337,
            boolProperty = false
        };

        return myNewObject;
    }

    public T Cast<T>(object obj, T type)
    {
        return (T)obj;
    }

Vous pouvez maintenant faire ceci:

var obj = MyMethod();
var myNewObj = Cast(obj, new { stringProperty = "", intProperty = 0, boolProperty = false });

myNewObj sera désormais un objet du même type que le type anonyme.

Autres conseils

La fonctionnalité linguistique dont vous avez besoin est la suivante:

public var GetMe()
{
    return new { FirstName = "John", LastName = "Smith" };
}

C'est-à-dire que var serait valide en tant que type de retour de méthode et que le compilateur déduirait le type réel de tout ce qui est retourné. Vous devrez alors le faire sur le site de l’appel:

var me = GetMe();

Deux types anonymes ayant des membres du même type seraient du même type. Par conséquent, si vous écriviez d'autres fonctions renvoyant le même modèle, elles auraient le même type. Pour tous les types A et B où B a un sous-ensemble des membres de A, alors A est compatible avec l'attribution avec B (B est comme une classe de base de A). Si vous avez écrit:

public var GetMeFrom(var names)
{
    return new { FirstName = names["First"], LastName = names["Last"] };
}

Le compilateur définirait effectivement cela comme une méthode générique avec deux paramètres de type, T1 étant le type de noms et T2 étant le type renvoyé par l'indexeur le T1 qui accepte une chaîne. T1 serait contraint de sorte qu'il doit avoir un indexeur qui accepte une chaîne. Et sur le site d’appel, vous transmettez tout ce qui a un indexeur qui accepte une chaîne et renvoie le type que vous aimez , et qui détermine le type de FirstName et Nom dans le type renvoyé par GetMeFrom .

Ainsi, l'inférence de type comprendrait tout cela pour vous, en capturant automatiquement les contraintes de type découvrables à partir du code.

À mon humble avis, le problème fondamental n’a rien à voir avec les types anonymes, mais le fait de déclarer une classe est trop détaillé.

Option 1:

Si vous pouviez déclarer une classe comme celle-ci:

public class MyClass
{ properties={ int Number, int Square, int Absolute, IEnumerable<int> Range } }

ou un autre moyen rapide similaire (comme l'exemple du tuple), vous ne ressentirez pas le besoin de faire des choses hacky avec des types anonymes uniquement pour enregistrer du code.

Lorsque le 'compilateur en tant que service' arrivera en C # 5, il devrait pouvoir l'intégrer avec succès et nous pourrons utiliser la métaprogrammation pour résoudre ce type de problèmes. Faites la fête comme en 1958!

Option 2:

Alternativement, en C # 4, vous pouvez simplement passer un type anonyme en tant que dynamique et éviter toute diffusion. Bien sûr, cela vous ouvre des erreurs d'exécution si vous renommez une variable, etc.

Option 3:

Si C # implémentait les génériques de la même manière que C ++, vous pourriez alors passer le type anonyme à une méthode, et tant qu'il aurait les bons membres, il ne ferait que compiler. Vous obtiendrez tous les avantages de la sécurité de type statique et aucun des inconvénients. Chaque fois que je dois taper où T: IS quelque chose en C #, je suis ennuyé de ne pas avoir fait cela!

Ce que vous décrivez (types nommés anonymes) sont essentiellement des "types de tuples".

Je pense qu'ils seraient un ajout intéressant à C #.

Si je concevais une telle fonctionnalité pour C #, je l'exposerais en utilisant une syntaxe comme celle-ci:

tuple<int x, int y>

pour que vous puissiez faire:

public tuple<int x, int y> GetStuff()
{
}

Je changerais alors la définition des types anonymes, de sorte que:

new { x = 2, y = 2}

avait tuple < int x, int y > en tant que type, plutôt qu'un type anonyme.

Faire en sorte que cela fonctionne avec le CLR actuel est un peu délicat, car une fois que vous pouvez nommer un type anonyme dans les signatures publiques, vous devez pouvoir les unifier dans des assemblys compilés séparément. Cela peut être accompli en incorporant un "constructeur de module". à l'intérieur de tout assemblage utilisant un type de tuple. Voir ce message pour un exemple.

Le seul inconvénient de cette approche est qu’elle ne respecte pas les règles "lazy" du CLR. modèle pour la génération de type. Cela signifie que les assemblages utilisant de nombreux types de tuple distincts peuvent rencontrer des types de charge légèrement plus lents. Une meilleure approche serait d’ajouter directement au CLR la prise en charge des types de tuple.

Mais, mis à part le changement de CLR, je pense que l'approche du constructeur de modules est la meilleure façon de faire quelque chose comme ça.

J'adorerais cette fonctionnalité, il y a eu de nombreuses fois où je l'ai voulue.

Un bon exemple est le traitement de XML. Vous les analysez pour récupérer un objet, mais vous devez ensuite en créer une version concrète à renvoyer à un appelant. Souvent, vous obtenez un code XML qui change considérablement et qui nécessite de nombreuses classes pour le gérer. Ne serait-il pas merveilleux de pouvoir simplement construire l'objet en utilisant LinqToXml en tant que var, puis de le renvoyer?

Je pense que ce serait une bonne magie de compilation pour les tuples:

Création d'un tuple:

(int, string, Person) tuple = (8, "hello", new Person());

équivalent à:

Tuple<int,string,Person> tuple = new Tuple<int,string,Person>(8 ,"hello", new Person());

Dans une fonction:

public (int, string, Person) GetTuple(){
    return ...
}

Obtenir des valeurs:

int number = tuple[1];
string text = tuple[2];
Person person = tuple[3];

Pourriez-vous créer une interface avec les propriétés FirstName et LastName et l'utiliser?

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top