Подтвердить что ты не робот

Возвращение анонимного типа в С#

У меня есть запрос, который возвращает анонимный тип, а запрос - в методе. Как вы пишете это:

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

      return "TheAnonymousType";
    }
}
4b9b3361

Ответ 1

Вы не можете.

Вы можете возвращать только object или контейнер объектов, например. IEnumerable<object>, IList<object> и т.д.

Ответ 2

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

Ответ 3

Вы не можете возвращать анонимные типы. Можете ли вы создать модель, которую можно вернуть? В противном случае вы должны использовать 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

Ответ 4

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

Возможно, вам придется установить пакет nuget System.ValueTuple.

Ответ 5

Вы можете использовать класс 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

Ответ 6

Компилятор С# является двухфазным компилятором. На первом этапе он просто проверяет пространства имен, иерархии классов, сигнатуры методов и т.д. Органы методов компилируются только во время второй фазы.

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

Таким образом, компилятор не имеет способа определить возвращаемый тип метода во время первой фазы.

Вот почему анонимные типы не могут использоваться в качестве типа возврата.

Как и другие, если вы используете .net 4.0 или терр, вы можете использовать Dynamic.

Если бы я был вами, я бы, вероятно, создал тип и вернул этот тип из метода. Таким образом, для будущих программистов легко сохранить свой код и прочитать его.

Ответ 7

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

Option1:

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

и вы сможете перебирать его как динамический объект и напрямую обращаться к своим свойствам.

Ответ 8

В этом случае вы можете вернуть список объектов.

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

      return TheQueryFromDB ;
    }
}

Ответ 9

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, хотя он все равно не позволяет вам видеть, что внутри переменной анонимного типа.

Я сталкивался с чем-то подобным раньше, пока я пытался реорганизовать какой-то код, вы можете проверить его здесь: Рефакторинг и создание отдельных методов

Ответ 10

Используя С# 7.0, мы все еще не можем возвращать анонимные типы, но у нас есть поддержка типов кортежей, и, таким образом, мы можем возвращать коллекцию tuple (System.ValueTuple<T1,T2> в этом случае).

Самая короткая версия кода, который вы хотите, может выглядеть так:

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

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

Или используя свободный синтаксис Linq:

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

Используя С# 7.1, мы можем опустить имена свойств кортежа, и они будут выведены из инициализации кортежа, как это работает с анонимными типами:

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

Ответ 11

С отражением.

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

Ответ 12

Вы можете использовать только динамическое ключевое слово,

   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 и т. Д...

Ответ 13

Другим вариантом может быть использование automapper: вы будете конвертировать в любой тип из вашего анонимного возвращаемого объекта, если длинные открытые свойства совпадают. Ключевыми моментами, возвращающими объект, являются 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; }
        }
    }

}

Ответ 14

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

Так что, если ваша цель состояла в том, чтобы запустить другую логику в одних и тех же источниках и иметь возможность объединить результаты в один список. Не уверен, какой нюанс этого не хватает для достижения поставленной цели, но пока вы возвращаете 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);
}

Ответ 15

Это еще один пример сумасшедших разработчиков на С#.