C # 'da anonim tür döndürülüyor


102

Anonim bir tür döndüren bir sorgum var ve sorgu bir yöntemde. Bunu nasıl yazarsın:

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

      return "TheAnonymousType";
    }
}

5
Neden anonim bir türü iade etmek istersiniz? Bu sonucu başka bir yerde nasıl kullanabilirsin?
Yuck


5
@Yuck ya json ya da c # türünün önemli olmadığı bir şeyi iade ediyorsanız
aw04

10
Bu sorunun mantık dışı olduğunu sanmıyorum. Aslında bunu birkaç kez yapmam gerekti. Varlık çerçevesini kullanırken daha belirgindir ve sorgunuzu tek bir işlevde yapmak ve sonuçları birkaç yerde kullanmak istiyorsanız. Sonuçları ekranda görüntülerken ve daha sonra aynı sonuçları bir raporda veya excel'e aktarırken kullanmaya ihtiyaç duyduğumda buna oldukça sık ihtiyacım oluyor. Sorgu, kullanıcı arayüzünden çok sayıda filtre ve benzeri içerebilir. aynı sorguyu birkaç yerde gerçekten oluşturmak istemiyorsunuz veya sonuçlara eklemek istediğinizde senkronizasyondan kolayca çıkabilirsiniz
Kevbo

Yanıtlar:


95

Yapamazsın.

Yalnızca dönebilir object, örneğin veya nesnelerin konteyner IEnumerable<object>, IList<object>vb


52
Veya dynamic. Bu, çalışmayı çok az da olsa kolaylaştırır.
vcsjones

ah tamam, yani yalnızca bir yöntem içinde anonim türleri kullanabilirsiniz, ancak dönüş değerleri olarak kullanamazsınız?
fransız

2
@frenchie: Evet, yalnızca üyenin vücudunun içinde. İade etmek istiyorsanız - iyi bilinen bir tür yapın.
abatishchev

11
Dinamik kullanmak bir çözüm değildir, anonim bir türdeki alanlar herkese açık değildir, bunlar dahilidir.
Hans Passant

7
@HansPassant Arayanın aynı mecliste olduğunu varsayarsak, yine de (biraz) yararlıdır. Değeri ne olursa olsun, alanlar herkese açıktır - tür içseldir. Ben genellikle kampta anonim bir tiple geri dönmemen gerekir.
vcsjones

42

Geri dönebilirsiniz, dynamicbu size anonim türün çalışma zamanı kontrollü bir sürümünü verecektir, ancak yalnızca .NET 4+


31

C # 7'de bunu başarmak için tupleları kullanabiliriz:

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

System.ValueTupleYine de nuget paketini yüklemeniz gerekebilir.


28

Anonim türleri döndüremezsiniz. İade edilebilecek bir model oluşturabilir misiniz? Aksi takdirde, bir object.

İşte konuyla ilgili Jon Skeet'in yazdığı bir makale

Makaledeki kod:

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

Veya işte benzer başka bir makale

Veya başkalarının yorum yaptığı gibi kullanabilirsiniz dynamic


8
Elbette bir tür oluşturabilirim; Bunu yapmaktan kaçınmaya çalışıyordum.
fransız

ilk bağlantı öldü, başka bir yere aktarılıp aktarılmadığını bilmiyor musunuz?
Rémi

17

Döndürmek gerektiğinde Tuple sınıfını anonim türlerin yerine kullanabilirsiniz:

Not: Tuple en fazla 8 parametreye sahip olabilir.

return Tuple.Create(variable1, variable2);

Veya orijinal gönderideki örnek için:

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


10

C # derleyicisi iki aşamalı bir derleyicidir. İlk aşamada sadece ad alanlarını, sınıf hiyerarşilerini, Yöntem imzalarını vb. Kontrol eder. Yöntem gövdeleri yalnızca ikinci aşamada derlenir.

Anonim türler, yöntem gövdesi derlenene kadar belirlenmez.

Bu nedenle, derleyicinin ilk aşamada yöntemin dönüş türünü belirleme yolu yoktur.

Anonim türlerin dönüş türü olarak kullanılamamasının nedeni budur.

.Net 4.0 veya üstü kullanıyorsanız, başkalarının da önerdiği gibi kullanabilirsiniz Dynamic.

Senin yerinde olsaydım, muhtemelen bir tür oluşturur ve bu türü yöntemden döndürürdüm. Bu şekilde, kodunuzu koruyan ve daha okunaklı olan gelecekteki programcılar için kolaylık sağlar.


8

Üç seçenek:

Seçenek 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;
   } 
}

Seçenek 2:

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

nesne olarak yineleyebilirsin

3. Seçenek:

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

ve onu dinamik bir nesne olarak yineleyebilecek ve özelliklerine doğrudan erişebileceksiniz.


3

Bu durumda nesnelerin listesini döndürebilirsiniz.

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

      return TheQueryFromDB ;
    }
}

3

C # 7.0'ı kullanarak hala anonim türleri döndüremiyoruz, ancak tuple türlerini destekliyoruz ve bu nedenle tuple( System.ValueTuple<T1,T2>bu durumda) koleksiyonunu döndürebiliriz . Şu anda Tuple types ifade ağaçlarında desteklenmemektedir ve verileri belleğe yüklemeniz gerekir.

İstediğiniz kodun en kısa versiyonu şöyle görünebilir:

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

Veya akıcı Linq sözdizimini kullanarak:

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

C # 7.1 kullanarak tuple özellik adlarını çıkarabiliriz ve anonim türlerde çalıştığı gibi tuple ilklendirmesinden çıkarılırlar:

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

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

Kendi sınıfınızı oluşturmak ve onun için sorgulamak bildiğim en iyi çözümdür Bildiğim kadarıyla anonim tip dönüş değerlerini başka bir yöntemde kullanamazsınız, çünkü sadece tanınmayacak, ancak aynı şekilde kullanılabilirler. yöntem. Onları IQueryableveya olarak döndürürdüm IEnumerable, ancak yine de anonim tür değişkeninin içinde ne olduğunu görmenize izin vermiyor.

Daha önce bir kodu yeniden düzenlemeye çalışırken buna benzer bir şeyle karşılaştım, buradan kontrol edebilirsiniz: Yeniden düzenleme ve ayrı yöntemler oluşturma


2

Yansımayla.

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

Örneklem:

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

Çıktı:

test2

1

Yalnızca dinamik anahtar kelime kullanabilirsiniz,

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

Ancak dinamik tür anahtar kelime ile derleme zamanı güvenliğini, IDE IntelliSense vb. Kaybedersiniz.


0

Başka bir seçenek de otomatikleştirici kullanmak olabilir: Genel özellikler eşleştiği sürece anonim olarak döndürülen nesnenizden herhangi bir türe dönüştürme yapacaksınız. Kilit noktalar, nesneyi döndürmek, linq ve autommaper'ı kullanmaktır. (veya serileştirilmiş json vb. döndüren benzer bir fikir kullanın veya yansıma kullanın ..)

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

Şimdi özellikle yerel işlevlerle, ancak anonim türü yapan bir temsilci geçerek bunu her zaman yapabilirsiniz.

Yani amacınız aynı kaynaklar üzerinde farklı mantık çalıştırmak ve sonuçları tek bir listede birleştirebilmekse. Belirtilen hedefe ulaşmak için bunun hangi nüansın eksik olduğundan emin değilsiniz, ancak a'yı döndürüp Tbir delegeyi yapmak için geçtiğiniz sürece T, bir işlevden anonim bir türü döndürebilirsiniz.

// 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

Belirli bir kullanım durumunda bir yöntemden anonim bir türü döndürmek aslında mümkündür. Bir bakalım!

C # 7 ile, hafif bir kısıtlama ile gelse de, bir yöntemden anonim türleri döndürmek mümkündür. Yerel işlev adı verilen yeni bir dil özelliğini kullanacağız , bir dolaylama numarasıyla birlikte (başka bir yönlendirme katmanı herhangi bir programlama sorununu çözebilir, değil mi?).

İşte yakın zamanda belirlediğim bir kullanım örneği. Tüm yapılandırma değerlerini şuradan yükledikten sonra günlüğe kaydetmek istiyorumAppSettings . Neden? Çünkü eksik değerlerle ilgili olarak varsayılan değerlere dönen bazı mantık, biraz ayrıştırma vb. Mantığı uyguladıktan sonra değerleri günlüğe kaydetmenin kolay bir yolu, hepsini bir sınıfa koymak ve bir günlük dosyasına serileştirmektir (log4net kullanarak). Ayrıca ayarlarla uğraşmanın karmaşık mantığını özetlemek ve bunu onlarla yapmam gerekenden ayırmak istiyorum. Bunların hepsi, yalnızca tek bir kullanım için var olan adlandırılmış bir sınıf oluşturmadan!

Anonim bir tür oluşturan yerel bir işlev kullanarak bunun nasıl çözüleceğini görelim.

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

Anonim bir sınıf oluşturmayı başardım ve ayrıca karmaşık ayar yönetimi ile uğraşmanın mantığını CreateHttpClientkendi "ifadesi" içinde ve içinde özetledim. Bu, OP'nin tam olarak istediği şey olmayabilir, ancak şu anda modern C # ile mümkün olan anonim tiplerle hafif bir yaklaşımdır.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.