Anoniem type teruggeven in C#

Ik heb een query die een anoniem type retourneert en de query is in een methode. Hoe schrijf je dit:

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

Antwoord 1, autoriteit 100%

Dat kan niet.

U kunt alleen objectof een container met objecten retourneren, b.v. IEnumerable<object>, IList<object>, enz.


Antwoord 2, autoriteit 47%

U kunt Dynamicretourneren, wat u een runtime-gecontroleerde versie van het anonieme type geeft, maar alleen in .NET 4+


Antwoord 3, autoriteit 38%

In C# 7 kunnen we tuples gebruiken om dit te bereiken:

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();
  }
}

Misschien moet u het nuget-pakket System.ValueTupleinstalleren.


Antwoord 4, autoriteit 28%

Je kunt geen anonieme typen retourneren. Kun je een model maken dat kan worden geretourneerd? Anders moet u een objectgebruiken.

Hier is een artikel geschreven door Jon Skeet over het onderwerp

Code uit het artikel:

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);            
    }
}

Of hier is nog een soortgelijk artikel

Of, zoals anderen al zeggen, je zou Dynamic

. kunnen gebruiken


Antwoord 5, autoriteit 17%

Je kunt de Tuple-klasse gebruiken als vervanging voor anonieme typen wanneer retourneren nodig is:

Opmerking: Tuple kan maximaal 8 parameters hebben.

return Tuple.Create(variable1, variable2);

Of, voor het voorbeeld uit de originele post:

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


Antwoord 6, autoriteit 12%

C#-compiler is een compiler in twee fasen. In de eerste fase controleert het alleen naamruimten, klassenhiërarchieën, methodehandtekeningen enz. Methodelichamen worden pas in de tweede fase gecompileerd.

Anonieme typen worden pas bepaald als de hoofdtekst van de methode is gecompileerd.

De compiler heeft dus geen manier om het retourtype van de methode tijdens de eerste fase te bepalen.

Dat is de reden waarom anonieme typen niet als retourtype kunnen worden gebruikt.

Zoals anderen hebben gesuggereerd als u .net 4.0 of rasp gebruikt, kunt u Dynamicgebruiken.

Als ik jou was, zou ik waarschijnlijk een type maken en dat type uit de methode retourneren. Op die manier is het voor de toekomstige programmeurs makkelijk om je code bij te houden en beter leesbaar.


Antwoord 7, autoriteit 9%

Drie opties:

Optie1:

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;
   } 
}

Optie 2:

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

je kunt het herhalen als object

Optie 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
   } 
}

en u kunt het herhalen als een dynamisch object en rechtstreeks toegang krijgen tot hun eigenschappen


Antwoord 8, autoriteit 5%

Met behulp van C# 7.0kunnen we nog steeds geen anonieme typen retourneren, maar we hebben ondersteuning voor tuple-typenen dus kunnen we een verzameling van tuple(System.ValueTuple<T1,T2>in dit geval). Momenteel worden Tuple typesniet ondersteund in expressiestructurenen jij gegevens in het geheugen moeten laden.

De kortste versie van de gewenste code kan er als volgt uitzien:

public IEnumerable<(int SomeVariable, object AnotherVariable)> TheMethod()
{
    ...
    return (from data in TheDC.Data
        select new { data.SomeInt, data.SomeObject }).ToList()
        .Select(data => (SomeVariable: data.SomeInt, AnotherVariable: data.SomeObject))
}

Of gebruik de vloeiende Linq-syntaxis:

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

Met C# 7.1kunnen we eigenschappennamen van tuple weglaten en deze worden afgeleid uit tuple-initialisatie zoals het werkt met anonieme typen:

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

Antwoord 9, autoriteit 2%

In dit geval kunt u een lijst met objecten retourneren.

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

Antwoord 10, autoriteit 2%

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;}
}

Je eigen klasse maken en ernaar zoeken is de beste oplossing die ik ken. Voor zover ik weet, kun je geen anonieme typeretourwaarden in een andere methode gebruiken, omdat het niet alleen wordt herkend. Ze kunnen echter worden gebruikt op dezelfde manier.
Ik gaf ze altijd terug als IQueryableof IEnumerable, hoewel het je nog steeds niet laat zien wat er in de anonieme typevariabele zit.

Ik kwam eerder zoiets tegen toen ik code probeerde te refactoren, je kunt het hier controleren: Refactoring en het creëren van aparte methoden


Antwoord 11

U kunt alleen dynamische zoekwoorden gebruiken,

  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};
   }

Maar met een zoekwoord van het dynamische type verliest u de veiligheid tijdens het compileren, IDE IntelliSense enz…


Antwoord 12

Met reflectie.

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();
}

Voorbeeld:

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

Uitvoer:

test2

Antwoord 13

Het is in feite mogelijk om een ​​anoniem type van een methode in een bepaalde use-case te retourneren. Laten we eens kijken!

Met C# 7 is het mogelijk om anonieme typen van een methode te retourneren, hoewel dit met een kleine beperking gepaard gaat. We gaan een nieuwe taalfunctie gebruiken genaamd lokale functiesamen met een indirecte truc (een andere laag van indirectie kan elke programmeeruitdaging oplossen, toch?).

Hier is een use-case die ik onlangs heb geïdentificeerd. Ik wil alle configuratiewaarden loggen nadat ik ze heb geladen vanuit AppSettings. Waarom? Omdat er enige logica is rond ontbrekende waarden die terugkeren naar standaardwaarden, wat parsing enzovoort. Een gemakkelijke manier om de waarden te loggen nadat de logica is toegepast, is door ze allemaal in een klasse te plaatsen en deze in een logbestand te serialiseren (met behulp van log4net). Ik wil ook de complexe logica van het omgaan met instellingen inkapselen en dat scheiden van alles wat ik ermee moet doen. Allemaal zonder een benoemde klasse te maken die alleen voor eenmalig gebruik bestaat!

Laten we kijken hoe we dit kunnen oplossen met een lokale functie die een anoniem type maakt.

public static HttpClient CreateHttpClient()
{
    // I deal with configuration values in this slightly convoluted way.
    // The benefit is encapsulation of logic and we do not need to
    // create a class, as we can use an anonymous class.
    // The result resembles an expression statement that
    // returns a value (similar to expressions in F#)
    var config = Invoke(() =>
    {
        // slightly complex logic with default value
        // in case of missing configuration value
        // (this is what I want to encapsulate)
        int? acquireTokenTimeoutSeconds = null;
        if (int.TryParse(ConfigurationManager.AppSettings["AcquireTokenTimeoutSeconds"], out int i))
        {
            acquireTokenTimeoutSeconds = i;
        }
        // more complex logic surrounding configuration values ...
        // construct the aggregate configuration class as an anonymous type!
        var c = new
        {
            AcquireTokenTimeoutSeconds =
                acquireTokenTimeoutSeconds ?? DefaultAcquireTokenTimeoutSeconds,
            // ... more properties
        };
        // log the whole object for monitoring purposes
        // (this is also a reason I want encapsulation)
        Log.InfoFormat("Config={0}", c);
        return c;
    });
    // use this configuration in any way necessary...
    // the rest of the method concerns only the factory,
    // i.e. creating the HttpClient with whatever configuration
    // in my case this:
    return new HttpClient(...);
    // local function that enables the above expression
    T Invoke<T>(Func<T> func) => func.Invoke();
}

Ik ben erin geslaagd een anonieme klasse te construeren en heb ook de logica van het omgaan met complex instellingenbeheer ingekapseld, allemaal binnen CreateHttpClienten binnen zijn eigen “expressie”. Dit is misschien niet precies wat de OP wilde, maar het is een lichtgewicht benadering met anonieme typen die momenteel mogelijk is in moderne C#.


Antwoord 14

Een andere optie zou het gebruik van automapper kunnen zijn: u converteert naar elk type van uw anonieme geretourneerde object zolang de openbare eigenschappen overeenkomen.
De belangrijkste punten zijn het retourneren van objecten, het gebruik van linq en automaper.
(of gebruik een soortgelijk idee om geserialiseerde json terug te geven, enz. of gebruik reflectie..)

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; }
        }
    }
}

Antwoord 15

Nu vooral met lokale functies, maar je kunt het altijd doen door een gemachtigde door te geven die het anonieme type maakt.

Dus als je doel was om verschillende logica op dezelfde bronnen uit te voeren en de resultaten in één lijst te kunnen combineren. Ik weet niet zeker welke nuance dit mist om het gestelde doel te bereiken, maar zolang u een Tretourneert en een gemachtigde doorgeeft om Tte maken, kunt u een anoniem type retourneren van een functie.

// 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);
}

Antwoord 16

Ik ben niet zeker van de naam van deze constructie, ik dacht dat dit een anoniem type werd genoemd, maar ik kan het mis hebben. Hoe dan ook, ik zocht dit:

private (bool start, bool end) InInterval(int lower, int upper)
{
    return (5 < lower && lower < 10, 5 < upper && upper < 10);
}
private string Test()
{
    var response = InInterval(2, 6);
    if (!response.start && !response.end)
        return "The interval did not start nor end inside the target";
    else if (!response.start)
        return "The interval did not start inside the target";
    else if (!response.end)
        return "The interval did not end inside the target";
    else
        return "The interval is inside the target";
}

Een andere stijl om dit te noemen zou kunnen zijn:

   var (start, end) = InInterval(2, 6);

Other episodes