web-dev-qa-db-fra.com

Existe-t-il un moyen de retourner le type anonyme à partir de la méthode?

Je sais que je ne peux pas écrire une méthode comme:

public var MyMethod()
{
   return new{ Property1 = "test", Property2="test"};
}

Je peux le faire autrement:

public object MyMethod()
{
   return new{ Property1 = "test", Property2="test"}
}

mais je ne veux pas faire la deuxième option parce que, si je le fais, je devrai utiliser la réflexion.


Pourquoi je veux faire ça:

Aujourd'hui, j'ai une méthode à l'intérieur de ma page aspx qui retourne un datatable comme résultat et je ne peux pas le changer, j'essayais de convertir ce DataTable en une méthode anonyme avec les propriétés avec lesquelles je veux travailler. Je ne voulais pas créer une classe uniquement pour cela et comme je devrai exécuter la même requête plus d'une fois, j'ai pensé que créer une méthode qui retourne un type anonyme serait une bonne idée.

63
Cleiton

Le renvoyer en tant que System.Object est la seule façon de renvoyer un type anonyme à partir d'une méthode. Malheureusement, il n'y a pas d'autre moyen de le faire, car les types anonymes ont été conçus spécifiquement pour empêcher leur utilisation de cette manière.

Il y a quelques astuces que vous pouvez faire en conjonction avec le retour d'un Object qui vous permettent de vous rapprocher. Si vous êtes intéressé par cette solution de contournement, veuillez lire Impossible de renvoyer le type anonyme de la méthode? Vraiment? .

Avertissement: Même si l'article que j'ai lié montre une solution de contournement qui ne signifie pas que c'est une bonne idée de le faire. Je vous déconseille fortement d'utiliser cette approche lorsque la création d'un type régulier serait plus sûre et plus facile à comprendre.

64
Andrew Hare

Alternativement, vous pouvez utiliser la classe Tuple dans .NET 4.0 et supérieur:

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

Tuple<string, string> Create()
{
return Tuple.Create("test1", "test2");
} 

alors vous pouvez accéder aux propriétés comme ceci:

var result = Create();
result.Item1;
result.Item2;
25
The Light
public object MyMethod() 
{
    return new
    {
         Property1 = "test",
        Property2 = "test"
     };
}

static void Main(..)
{
    dynamic o = MyMethod();  
    var p1 = o.Property1;
    var p2 = o.Property2;
}
18
Nana Kofi

La solution la plus simple consiste à créer une classe, à insérer les valeurs dans la propriété, puis à la renvoyer. Si les types anonymes rendent votre vie plus difficile, vous ne les utilisez pas correctement.

16
Jagd

Comme alternative, à partir de C # 7, nous pouvons utiliser ValueTuple . Un petit exemple de ici :

public (int sum, int count) DoStuff(IEnumerable<int> values) 
{
    var res = (sum: 0, count: 0);
    foreach (var value in values) { res.sum += value; res.count++; }
    return res;
}

Et du côté de la réception:

var result = DoStuff(Enumerable.Range(0, 10));
Console.WriteLine($"Sum: {result.Sum}, Count: {result.Count}");

Ou:

var (sum, count) = DoStuff(Enumerable.Range(0, 10));
Console.WriteLine($"Sum: {sum}, Count: {count}");
13
baur

Nonobstant les avertissements à savoir si c'est une bonne idée ou non ... Un dynamic semble très bien fonctionner pour une méthode privée.

void Main()
{
    var result = MyMethod();
    Console.WriteLine($"Result: {result.Property1}, {result.Property2}");
}

public dynamic MyMethod()
{
    return new { Property1 = "test1", Property2 = "test2" };
}

Vous pouvez exécuter cet exemple dans LinqPad. Il affichera:

Résultat: test1, test2

8
controlbox

Non, les types anonymes ne peuvent pas exister en dehors du contexte dans lequel ils sont créés et, par conséquent, ne peuvent pas être utilisés comme type de retour de méthode. Vous pouvez renvoyer l'instance sous la forme d'un object, mais c'est une bien meilleure idée de créer explicitement votre propre type de conteneur à cet effet.

3
Adam Robinson

Je pense qu'Andrew Hare a raison, il faudrait juste renvoyer "objet". Pour un commentaire éditorial, j'ai envie de traiter des objets bruts en OO code peut être une "odeur de code". Il y a des cas où c'est la bonne chose à faire, mais la plupart du temps, vous serait mieux de définir une interface à retourner, ou d'utiliser une sorte de type de classe de base, si vous allez retourner des types liés.

3
Andy White

Désolé, vous n'êtes vraiment pas censé faire ça. Vous pouvez le pirater avec réflexion ou en créant une méthode d'assistance générique pour renvoyer le type pour vous, mais cela fonctionne vraiment contre le langage. Déclarez simplement le type pour qu'il soit clair de ce qui se passe.

2
mqp

Non, il n'est pas possible d'étendre la portée de la classe anonyme en dehors de la méthode. En dehors de la méthode, la classe est vraiment anonyme et la réflexion est le seul moyen d'accéder à ses membres.

2
Guffa

Vous pouvez également inverser votre flux de contrôle si possible:

    public abstract class SafeAnon<TContext>
    {
        public static Anon<T> Create<T>(Func<T> anonFactory)
        {
            return new Anon<T>(anonFactory());
        }

        public abstract void Fire(TContext context);
        public class Anon<T> : SafeAnon<TContext>
        {
            private readonly T _out;

            public delegate void Delayed(TContext context, T anon);

            public Anon(T @out)
            {
                _out = @out;
            }

            public event Delayed UseMe;
            public override void Fire(TContext context)
            {
                UseMe?.Invoke(context, _out);
            }
        }
    }

    public static SafeAnon<SomeContext> Test()
    {
        var sa = SafeAnon<SomeContext>.Create(() => new { AnonStuff = "asdf123" });

        sa.UseMe += (ctx, anon) =>
        {
            ctx.Stuff.Add(anon.AnonStuff);
        };

        return sa;
    }

    public class SomeContext
    {
        public List<string> Stuff = new List<string>();
    }

puis plus tard ailleurs:

    static void Main()
    {
        var anonWithoutContext = Test();

        var nowTheresMyContext = new SomeContext();
        anonWithoutContext.Fire(nowTheresMyContext);

        Console.WriteLine(nowTheresMyContext.Stuff[0]);

    }
2
sneusse