web-dev-qa-db-fra.com

Renvoi de type anonyme en C #

J'ai une requête qui retourne un type anonyme et la requête est dans une méthode. Comment écrivez-vous ceci:

public "TheAnonymousType" TheMethod(SomeParameter)
{
  using (MyDC TheDC = new MyDC())
  {
     var TheQueryFromDB = (....
                           select new { SomeVariable = ....,
                                        AnotherVariable = ....}
                           ).ToList();

      return "TheAnonymousType";
    }
}
79
frenchie

Tu ne peux pas.

Vous pouvez uniquement renvoyer object ou un conteneur d'objets, par exemple. IEnumerable<object>, IList<object>, etc.

73
abatishchev

Vous pouvez retourner dynamic, ce qui vous donnera une version du type anonyme vérifiée à l'exécution, mais uniquement dans .NET 4+. 

35
Luke McGregor

Vous ne pouvez pas renvoyer de types anonymes. Pouvez-vous créer un modèle qui peut être retourné? Sinon, vous devez utiliser une variable object.

Voici un article écrit par Jon Skeet sur le sujet

Code de l'article:

using System;

static class GrottyHacks
{
    internal static T Cast<T>(object target, T example)
    {
        return (T) target;
    }
}

class CheesecakeFactory
{
    static object CreateCheesecake()
    {
        return new { Fruit="Strawberry", Topping="Chocolate" };
    }

    static void Main()
    {
        object weaklyTyped = CreateCheesecake();
        var stronglyTyped = GrottyHacks.Cast(weaklyTyped,
            new { Fruit="", Topping="" });

        Console.WriteLine("Cheesecake: {0} ({1})",
            stronglyTyped.Fruit, stronglyTyped.Topping);            
    }
}

Ou, voici un autre article similaire

Ou, comme d'autres commentent, vous pouvez utiliser dynamic

24
Justin Pihony

Vous pouvez utiliser la classe Tuple comme substitut d'un type anonyme lorsque le renvoi est nécessaire:

Remarque: Tuple peut avoir jusqu'à 8 paramètres.

return Tuple.Create(variable1, variable2);

Ou, pour l'exemple du post original:

public List<Tuple<SomeType, AnotherType>> TheMethod(SomeParameter)
{
  using (MyDC TheDC = new MyDC())
  {
     var TheQueryFromDB = (....
                           select Tuple.Create(..., ...)
                           ).ToList();

      return TheQueryFromDB.ToList();
    }
}

http://msdn.Microsoft.com/en-us/library/system.Tuple(v=vs.110).aspx

16
craigrs84

En C # 7, nous pouvons utiliser des n-uplets pour accomplir ceci:

public List<(int SomeVariable, string AnotherVariable)> TheMethod(SomeParameter)
{
  using (MyDC TheDC = new MyDC())
  {
     var TheQueryFromDB = (....
                       select new { SomeVariable = ....,
                                    AnotherVariable = ....}
                       ).ToList();

      return TheQueryFromDB.Select(s => (SomeVariable = s.SomeVariable, AnotherVariable = s.AnotherVariable)).ToList();
  }
}

Cependant, vous devrez peut-être installer le paquetage System.ValueTuple nuget.

12
Rosdi Kasim

Le compilateur C # est un compilateur à deux phases. Dans la première phase, il vérifie uniquement les espaces de noms, les hiérarchies de classes, les signatures de méthodes, etc. Les corps de méthodes ne sont compilés que pendant la deuxième phase.

Les types anonymes ne sont pas déterminés tant que le corps de la méthode n'est pas compilé. 

Le compilateur n'a donc aucun moyen de déterminer le type de retour de la méthode au cours de la première phase.

C'est la raison pour laquelle les types anonymes ne peuvent pas être utilisés comme type de retour.

Comme d'autres l'ont suggéré, si vous utilisez .net 4.0 ou râpe, vous pouvez utiliser Dynamic.

Si j'étais vous, je créerais probablement un type et renverrais ce type à partir de la méthode. De cette façon, il est facile pour les futurs programmeurs qui maintiennent votre code et plus lisible.

9
Sandeep

Trois options:

Option 1:

public class TheRepresentativeType {
    public ... SomeVariable {get;set;}
    public ... AnotherVariable {get;set;}
}

public IEnumerable<TheRepresentativeType> TheMethod(SomeParameter)
{
   using (MyDC TheDC = new MyDC())
   {
     var TheQueryFromDB = (....
                           select new TheRepresentativeType{ SomeVariable = ....,
                                        AnotherVariable = ....}
                           ).ToList();

     return TheQueryFromDB;
   } 
}

Option 2:

public IEnumerable TheMethod(SomeParameter)
{
   using (MyDC TheDC = new MyDC())
   {
     var TheQueryFromDB = (....
                           select new TheRepresentativeType{ SomeVariable = ....,
                                        AnotherVariable = ....}
                           ).ToList();
     return TheQueryFromDB;
   } 
}

vous pouvez le parcourir comme objet

Option 3:

public IEnumerable<dynamic> TheMethod(SomeParameter)
{
   using (MyDC TheDC = new MyDC())
   {
     var TheQueryFromDB = (....
                           select new TheRepresentativeType{ SomeVariable = ....,
                                        AnotherVariable = ....}
                           ).ToList();

     return TheQueryFromDB; //You may need to call .Cast<dynamic>(), but I'm not sure
   } 
}

et vous pourrez le parcourir comme un objet dynamique et accéder directement à leurs propriétés

8
Ivo

Vous pouvez retourner une liste d'objets dans ce cas.

public List<object> TheMethod(SomeParameter)
{
  using (MyDC TheDC = new MyDC())
  {
     var TheQueryFromDB = (....
                           select new { SomeVariable = ....,
                                        AnotherVariable = ....}
                           ).ToList();

      return TheQueryFromDB ;
    }
}
2
Chuck Norris

Avec réflexion.

public object tst() {
    var a = new {
        prop1 = "test1",
        prop2 = "test2"
    };

    return a;
}


public string tst2(object anonymousObject, string propName) {
    return anonymousObject.GetType().GetProperties()
        .Where(w => w.Name == propName)
        .Select(s => s.GetValue(anonymousObject))
        .FirstOrDefault().ToString();
}

Échantillon:

object a = tst();
var val = tst2(a, "prop2");

Sortie:

test2
2
Mehmet YAŞAR
public List<SomeClass> TheMethod(SomeParameter)
{
  using (MyDC TheDC = new MyDC())
  {
     var TheQueryFromDB = (....
                           select new SomeClass{ SomeVariable = ....,
                                        AnotherVariable = ....}
                           ).ToList();

      return TheQueryFromDB.ToList();
    }
}

public class SomeClass{
   public string SomeVariable{get;set}
   public string AnotherVariable{get;set;}
}

Créer votre propre classe et interroger cette dernière est la meilleure solution que je connaisse. Autant que je sache, vous ne pouvez pas utiliser les valeurs renvoyées de type anonyme dans une autre méthode, car elle ne sera pas simplement reconnue. Cependant, elles peuvent être utilisées de la même manière. method . J'avais l'habitude de les renvoyer sous la forme IQueryable ou IEnumerable, bien que cela ne vous permette toujours pas de voir ce qu'il y a à l'intérieur de la variable de type anonyme.

Je rencontre quelque chose comme ceci avant alors que j'essayais de refactoriser du code, vous pouvez le vérifier ici: Refactoring et création de méthodes séparées

2
Bastardo

En utilisant C # 7.0 , nous ne pouvons toujours pas retourner les types anonymes mais nous avons un support de types de tuples et nous pouvons donc renvoyer une collection de Tuple (System.ValueTuple<T1,T2> dans ce cas). 

La version la plus courte du code que vous voulez peut ressembler à ceci:

public List<(int SomeVariable, object AnotherVariable)> TheMethod()
{
    ...

    return (from data in TheDC.Data
            select (SomeVariable: data.SomeInt, AnotherVariable: data.SomeObject)
            ).ToList();
}

Ou en utilisant la syntaxe fluide Linq:

return TheDC.Data
    .Select(data => (SomeVariable: data.SomeInt, AnotherVariable: data.SomeObject))
    .ToList();

En utilisant C # 7.1 nous pouvons omettre les noms de propriétés de Tuple et ils seront déduits de l’initialisation de Tuple comme si cela fonctionnait avec les types anonymes:

select (data.SomeInt, data.SomeObject)
// or
Select(data => (data.SomeInt, data.SomeObject))
2
AlbertK

Maintenant avec les fonctions locales en particulier, mais vous pouvez toujours le faire en passant un délégué qui crée le type anonyme. 

Donc, si votre objectif était d'exécuter une logique différente sur les mêmes sources et de pouvoir combiner les résultats dans une seule liste. Vous ne savez pas quelle nuance il manque pour atteindre l'objectif défini, mais tant que vous renvoyez un T et transmettez un délégué à make T, vous pouvez retourner un type anonyme à partir d'une fonction.

// returning an anonymous type
// look mom no casting
void LookMyChildReturnsAnAnonICanConsume()
{
    // if C# had first class functions you could do
    // var anonyFunc = (name:string,id:int) => new {Name=name,Id=id};
    var items = new[] { new { Item1 = "hello", Item2 = 3 } };
    var itemsProjection =items.Select(x => SomeLogic(x.Item1, x.Item2, (y, i) => new { Word = y, Count = i} ));
    // same projection = same type
    var otherSourceProjection = SomeOtherSource((y,i) => new {Word=y,Count=i});
    var q =
        from anony1 in itemsProjection
        join anony2 in otherSourceProjection
            on anony1.Word equals anony2.Word
        select new {anony1.Word,Source1Count=anony1.Count,Source2Count=anony2.Count};
    var togetherForever = itemsProjection.Concat(otherSourceProjection).ToList();
}

T SomeLogic<T>(string item1, int item2, Func<string,int,T> f){
    return f(item1,item2);
}
IEnumerable<T> SomeOtherSource<T>(Func<string,int,T> f){
    var dbValues = new []{Tuple.Create("hello",1), Tuple.Create("bye",2)};
    foreach(var x in dbValues)
        yield return f(x.Item1,x.Item2);
}
0
Maslow

Une autre option pourrait être automapper: vous convertirez n'importe quel type à partir de votre objet renvoyé anonyme, car les propriétés publiques correspondantes correspondent à . json sérialisé, etc. ou utilisez la réflexion ..)

using System.Linq;
using System.Reflection;
using AutoMapper;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Newtonsoft.Json;

namespace UnitTestProject1
{
    [TestClass]
    public class UnitTest1
    {
        [TestMethod]
        public void TestMethod1()
        {
            var data = GetData();

            var firts = data.First();

            var info = firts.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public).First(p => p.Name == "Name");
            var value = info.GetValue(firts);

            Assert.AreEqual(value, "One");
        }


        [TestMethod]
        public void TestMethod2()
        {
            var data = GetData();

            var config = new MapperConfiguration(cfg => cfg.CreateMissingTypeMaps = true);
            var mapper = config.CreateMapper();

            var users = data.Select(mapper.Map<User>).ToArray();

            var firts = users.First();

            Assert.AreEqual(firts.Name, "One");

        }

        [TestMethod]
        public void TestMethod3()
        {
            var data = GetJData();


            var users = JsonConvert.DeserializeObject<User[]>(data);

            var firts = users.First();

            Assert.AreEqual(firts.Name, "One");

        }

        private object[] GetData()
        {

            return new[] { new { Id = 1, Name = "One" }, new { Id = 2, Name = "Two" } };
        }

        private string GetJData()
        {

            return JsonConvert.SerializeObject(new []{ new { Id = 1, Name = "One" }, new { Id = 2, Name = "Two" } }, Formatting.None);
        }

        public class User
        {
            public int Id { get; set; }
            public string Name { get; set; }
        }
    }

}
0
alhpe

Vous ne pouvez utiliser que mot clé dynamique,

   dynamic obj = GetAnonymousType();

   Console.WriteLine(obj.Name);
   Console.WriteLine(obj.LastName);
   Console.WriteLine(obj.Age); 


   public static dynamic GetAnonymousType()
   {
       return new { Name = "John", LastName = "Smith", Age=42};
   }

Mais avec un mot-clé de type dynamique, vous perdrez la sécurité du temps de compilation, IDE IntelliSense, etc.

0
Imran Jalali