Повернення анонімного типу в C #


100

У мене є запит, який повертає анонімний тип, і запит є методом. Як ти це пишеш:

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

      return "TheAnonymousType";
    }
}

5
Чому ви хочете повернути анонімний тип? Як ви могли використовувати цей результат деінде?
Юк


5
@Yuck, що якщо ви повертаєте json або щось там, де тип c # не має значення
aw04

10
Я не думаю, що це питання не є причиною. Мені справді потрібно було це зробити кілька разів. Це більш очевидно при використанні структури сутності, і ви хочете робити свій запит в одній функції і використовувати результати в декількох місцях. Мені це потрібно часто, коли відображаються результати на екрані, а потім потрібно використовувати ті самі результати у звіті або при експорті до excel. Запит може містити багато фільтрів і подібних з інтерфейсу користувача. ви не хочете створювати один і той же запит у кількох місцях, або ви можете легко вийти з синхронізації, коли хочете додати до результатів
Kevbo

Відповіді:


94

Ви не можете.

Ви можете тільки повернути objectабо контейнер об'єктів, наприклад IEnumerable<object>, IList<object>і т.д.


51
Або dynamic. Це полегшує роботу з будь-яким часом.
vcsjones

ах, так що ви можете використовувати лише анонімні типи в методі, але не як зворотні значення?
frenchie

2
@frenchie: Так, лише всередині тіла члена. Якщо ви хочете повернути його - зробіть його відомим типом.
абатищев

11
Використання динаміки не є рішенням, поля анонімного типу не є загальнодоступними, вони є внутрішніми.
Ганс Пасант

7
@HansPassant Якщо припустити, що абонент знаходиться в одній збірці, то це все-таки (дещо) корисно. Для чого це варто, поля публічні - тип внутрішній. Я, як правило, в таборі, що ви все одно не повинні повертати анонімний тип.
vcsjones

42

Ви можете повернутись, dynamicякий надасть вам перевірену версію анонімного типу, але лише в .NET 4+


30

У C # 7 ми можемо використовувати кортежі для цього:

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.ValueTupleпакунок нута.


27

Ви не можете повернути анонімні типи. Чи можете ви створити модель, яку можна повернути? В іншому випадку ви повинні використовувати object.

Ось стаття, написана Джоном Скітом на цю тему

Код із статті:

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

Або ось ще одна подібна стаття

Або, як коментують інші, ви могли б використовувати dynamic


8
Звичайно, я можу створити тип; Я шукав, щоб цього не робити.
frenchie

перше посилання мертве, хіба ти не випадково дізнаєшся, як це було передано в іншому місці?
Ремі

17

Ви можете використовувати клас Tuple як заміну анонімних типів, коли необхідне повернення:

Примітка: кортеж може мати до 8 параметрів.

return Tuple.Create(variable1, variable2);

Або, наприклад, з оригіналу публікації:

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 # - це двофазний компілятор. На першій фазі він просто перевіряє простори імен, ієрархії класів, підписи методів тощо. Методи тіла складаються лише під час другої фази.

Анонімні типи не визначаються до тих пір, поки не буде складено тіло методу.

Таким чином, у компілятора немає способу визначення типу повернення методу протягом першої фази.

Саме тому анонімні типи не можна використовувати як тип повернення.

Як рекомендують інші, якщо ви використовуєте .net 4.0 або терку, ви можете використовувати Dynamic.

Якби я був ти, я, певно, створив би тип і повернув би цей тип із методу. Таким чином, майбутнім програмістам легко буде підтримувати ваш код і читати.


8

Три варіанти:

Варіант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;
   } 
}

Варіант 2:

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

ви можете повторити його як об’єкт

Варіант 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
   } 
}

і ви зможете повторити його як динамічний об'єкт і отримати доступ до їх властивостей безпосередньо


3

У цьому випадку ви можете повернути список об’єктів.

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

      return TheQueryFromDB ;
    }
}

3

Використовуючи C # 7.0, ми все ще не можемо повернути анонімні типи, але у нас є підтримка типів кортежу, і, таким чином, ми можемо повернути колекцію tuple( System.ValueTuple<T1,T2>у цьому випадку). Наразі Tuple types в деревах вираження не підтримується, і вам потрібно завантажити дані в пам'ять.

Найкоротша версія потрібного коду може виглядати так:

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

Або використовуючи вільний синтаксис Linq:

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

Використовуючи C # 7.1, ми можемо опустити імена властивостей кортежу, і вони будуть зроблені з ініціалізації кортежу, як це працює з анонімними типами:

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

Створення власного класу та запит до нього - це найкраще рішення, яке я знаю. Наскільки я знаю, ви не можете використовувати анонімні типові значення повернення в іншому методі, тому що вони не будуть просто розпізнані. Однак вони можуть бути використані в тому ж самому метод. Я раніше повертав їх як IQueryableабо IEnumerable, хоча це все ще не дозволяє вам побачити, що знаходиться всередині змінної анонімного типу.

Я наткнувся на щось подібне раніше, поки я намагався перефактувати якийсь код, ви можете перевірити це тут: Перейменування та створення окремих методів


2

З роздумом.

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

Зразок:

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

Вихід:

test2

1

Ви можете використовувати лише динамічне ключове слово,

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

Але за допомогою ключового слова з динамічним типом ви втратите безпеку часу компіляції, IDE IntelliSense тощо.


0

Іншим варіантом може бути використання автоматичної машини: Ви будете перетворюватися на будь-який тип з анонімного повернутого об’єкта, коли довгі загальнодоступні властивості відповідають. Ключові моменти - повернення об'єкта, використання linq та autommaper. (або використовувати подібну ідею, що повертає серіалізований json тощо) або використовувати рефлексію ..)

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

Тепер особливо з локальними функціями, але ви завжди можете це зробити, передавши делегата, який робить анонімний тип.

Тож якщо вашою метою було запускати різні логіки в одних і тих же джерелах і мати можливість поєднувати результати в єдиний список. Не впевнений, якого нюансу цього не вистачає для досягнення поставленої мети, але доки ви повернете а Tта передасте делегата зробити T, ви можете повернути анонімний тип із функції.

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

Фактично можливо повернути анонімний тип із методу в конкретному випадку використання. Давай подивимось!

За допомогою C # 7 можна повернути анонімні типи з методу, хоча це має незначне обмеження. Ми будемо використовувати нову мовну функцію під назвою локальна функція разом з непрямим фокусом (інший шар непрямості може вирішити будь-яку проблему програмування, правда?).

Ось випадок використання, який я нещодавно визначив. Я хочу записати всі значення конфігурації після того, як я їх завантажив AppSettings. Чому? Оскільки існує якась логіка навколо відсутніх значень, які повертаються до значень за замовчуванням, деякі розбору тощо. Найпростіший спосіб реєстрації значень після застосування логіки - це розмістити їх у класі та серіалізувати їх у лог-файл (за допомогою log4net). Я також хочу прокласти складну логіку роботи з налаштуваннями і відокремити цю програму від того, що мені потрібно робити. Все без створення названого класу, який існує лише для одного використання!

Подивимося, як вирішити це за допомогою локальної функції, яка створює анонімний тип.

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

Мені вдалося створити анонімний клас, а також уклав логіку роботи зі складним управлінням налаштуваннями, всередині CreateHttpClientі всередині власного "вираження". Це може бути не саме те, чого хотіла ОП, але це легкий підхід з анонімними типами, який наразі можливий у сучасному C #.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.