web-dev-qa-db-fra.com

Comment passer des types anonymes en paramètres?

Comment puis-je transmettre des types anonymes en tant que paramètres à d'autres fonctions? Considérons cet exemple:

var query = from employee in employees select new { Name = employee.Name, Id = employee.Id };
LogEmployees(query);

La variable query ici n'a pas de type fort. Comment dois-je définir ma fonction LogEmployees pour l'accepter?

public void LogEmployees (? list)
{
    foreach (? item in list)
    {

    }
}

En d'autres termes, que devrais-je utiliser à la place de ? des notes.

122
Saeed Neamati

Je pense que vous devriez faire une classe pour ce type anonyme. Ce serait la chose la plus sensée à faire à mon avis. Mais si vous ne voulez vraiment pas, vous pouvez utiliser la dynamique:

public void LogEmployees (IEnumerable<dynamic> list)
{
    foreach (dynamic item in list)
    {
        string name = item.Name;
        int id = item.Id;
    }
}

Notez que ceci est not fortement typé. Ainsi, si, par exemple, Name change en EmployeeName, vous ne saurez pas qu'il y a un problème avant l'exécution.

157
Tim S.

Vous pouvez le faire comme ça:

public void LogEmployees<T>(List<T> list) // Or IEnumerable<T> list
{
    foreach (T item in list)
    {

    }
}

... mais vous ne pourrez pas faire grand chose avec chaque article. Vous pouvez appeler ToString, mais vous ne pourrez pas utiliser (par exemple) Name et Id directement.

39
Jon Skeet

Malheureusement, ce que vous essayez de faire est impossible. Sous le capot, la variable de requête est typée pour être un IEnumerable de type anonyme. Les noms de type anonymes ne peuvent pas être représentés dans le code utilisateur, il n'y a donc aucun moyen de les transformer en paramètre d'entrée pour une fonction.

La meilleure solution consiste à créer un type et à l'utiliser comme résultat de la requête, puis à le transmettre à la fonction. Par exemple,

struct Data {
  public string ColumnName; 
}

var query = (from name in some.Table
            select new Data { ColumnName = name });
MethodOp(query);
...
MethodOp(IEnumerable<Data> enumerable);

Dans ce cas, toutefois, vous ne sélectionnez qu'un seul champ. Il peut donc être plus simple de sélectionner le champ directement. La requête sera ainsi saisie sous la forme d'un IEnumerable du type de champ. Dans ce cas, nom de la colonne.

var query = (from name in some.Table select name);  // IEnumerable<string>
18
JaredPar

Vous ne pouvez pas transmettre un type anonyme à une fonction non générique, sauf si le type de paramètre est object.

public void LogEmployees (object obj)
{
    var list = obj as IEnumerable(); 
    if (list == null)
       return;

    foreach (var item in list)
    {

    }
}

Les types anonymes sont destinés à une utilisation à court terme dans une méthode.

De MSDN - Types anonymes :

Vous ne pouvez pas déclarer un champ, une propriété, un événement ou le type de retour d'une méthode comme ayant un type anonyme. De même, vous ne pouvez pas déclarer un paramètre formel d'une méthode, d'une propriété, d'un constructeur ou d'un indexeur comme ayant un type anonyme. Pour passer un type anonyme, ou une collection contenant des types anonymes, en tant qu'argument d'une méthode, vous pouvez déclarer le paramètre en tant que type object . Cependant, cela va à l'encontre du but de la frappe forte.

(c'est moi qui souligne)


Mise à jour

Vous pouvez utiliser des génériques pour obtenir ce que vous voulez:

public void LogEmployees<T>(IEnumerable<T> list)
{
    foreach (T item in list)
    {

    }
}
10
Oded

Normalement, vous faites cela avec des génériques, par exemple:

MapEntToObj<T>(IQueryable<T> query) {...}

Le compilateur devrait alors déduire le T lorsque vous appelez MapEntToObj(query). Vous ne savez pas trop ce que vous voulez faire dans la méthode, je ne peux donc pas dire si cela est utile ... le problème est que dans MapEntToObj, vous ne pouvez toujours pas nommer le T - vous pouvez soit:

  • appeler d'autres méthodes génériques avec T
  • utiliser la réflexion sur T pour faire les choses

mais à part ça, il est assez difficile de manipuler des types anonymes - notamment parce qu'ils sont immuables ;-p

Une autre astuce (quand extraire données) est de passer également un sélecteur - c’est-à-dire quelque chose comme:

Foo<TSource, TValue>(IEnumerable<TSource> source,
        Func<TSource,string> name) {
    foreach(TSource item in source) Console.WriteLine(name(item));
}
...
Foo(query, x=>x.Title);
7
Marc Gravell

Vous pouvez utiliser des génériques avec l’astuce suivante (conversion au type anonyme):

public void LogEmployees<T>(IEnumerable<T> list)
{
    foreach (T item in list)
    {
        var typedItem = Cast(item, new { Name = "", Id = 0 });
        // now you can use typedItem.Name, etc.
    }
}

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

"dynamique" peut également être utilisé à cette fin.

var anonymousType = new { Id = 1, Name = "A" };

var anonymousTypes = new[] { new { Id = 1, Name = "A" }, new { Id = 2, Name = "B" };

private void DisplayAnonymousType(dynamic anonymousType)
{
}

private void DisplayAnonymousTypes(IEnumerable<dynamic> anonymousTypes)
{
   foreach (var info in anonymousTypes)
   {

   }
}
6
Dinesh Kumar P

Au lieu de passer un type anonyme, passez une liste d'un type dynamique:

  1. var dynamicResult = anonymousQueryResult.ToList<dynamic>();
  2. Signature de la méthode: DoSomething(List<dynamic> _dynamicResult)
  3. Méthode d'appel: DoSomething(dynamicResult);
  4. terminé.

Merci à Petar Ivanov !

2
usefulBee

Si vous savez que vos résultats implémentent une certaine interface, vous pouvez l’utiliser comme type de données:

public void LogEmployees<T>(IEnumerable<T> list)
{
    foreach (T item in list)
    {

    }
}
0
Alex

J'utiliserais IEnumerable<object> comme type d'argument. Cependant, ce n’est pas un avantage considérable pour la distribution explicite inévitable. À votre santé

0
Mario Vernari