programing tip

C #에서 익명 형식 반환

itbloger 2020. 9. 7. 07:52
반응형

C #에서 익명 형식 반환


익명 형식을 반환하는 쿼리가 있고 쿼리가 메서드에 있습니다. 이것을 어떻게 작성합니까?

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

      return "TheAnonymousType";
    }
}

당신은 할 수 없습니다.

당신은 반환 할 수 있습니다 object, 예를 들어, 또는 객체의 용기 IEnumerable<object>, IList<object>


dynamic익명 유형의 런타임 확인 버전을 제공하지만 .NET 4+에서만 반환 수 있습니다.


익명 형식은 반환 할 수 없습니다. 반환 할 수있는 모델을 만들 수 있습니까? 그렇지 않으면 object.

다음은 주제에 대해 Jon Skeet이 작성한 기사입니다.

기사의 코드 :

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


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그래도 너겟 패키지 를 설치해야 할 수도 있습니다 .


반환이 필요한 경우 익명 형식 대신 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


C # 컴파일러는 2 단계 컴파일러입니다. 첫 번째 단계에서는 네임 스페이스, 클래스 계층 구조, 메서드 서명 등 만 확인합니다. 메서드 본문은 두 번째 단계에서만 컴파일됩니다.

익명 형식은 메서드 본문이 컴파일 될 때까지 결정되지 않습니다.

따라서 컴파일러는 첫 번째 단계에서 메서드의 반환 유형을 결정할 방법이 없습니다.

이것이 익명 유형을 반환 유형으로 사용할 수없는 이유입니다.

.net 4.0 또는 grater를 사용하는 경우 다른 사람들이 제안했듯이 Dynamic.

내가 당신이라면 아마도 유형을 만들고 메서드에서 해당 유형을 반환 할 것입니다. 이렇게하면 코드를 유지하고 더 읽기 쉬운 미래의 프로그래머가 쉽게 사용할 수 있습니다.


세 가지 옵션 :

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

동적 개체로 반복하고 속성에 직접 액세스 할 수 있습니다.


이 경우 개체 목록을 반환 할 수 있습니다.

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

      return TheQueryFromDB ;
    }
}

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했지만 여전히 익명 유형 변수의 내부를 볼 수는 없습니다.

I run into something like this before while I was trying to refactor some code, you can check it here : Refactoring and creating separate methods


With reflection.

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

Sample:

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

Output:

test2

Using C# 7.0 we still can't return anonymous types but we have a support of tuple types and thus we can return a collection of tuple (System.ValueTuple<T1,T2> in this case).

The shortest version of the code you want may look like this:

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

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

Or using the fluent Linq syntax:

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

Using C# 7.1 we can omit properties names of tuple and they will be inferred from tuple initialization like it works with anonymous types:

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

You can only use dynamic keyword,

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

But with dynamic type keyword you will loose compile time safety, IDE IntelliSense etc...


Another option could be using automapper: You will be converting to any type from your anonymous returned object as long public properties matches. The key points are, returning object, use linq and autommaper. (or use similar idea returning serialized json, etc. or use reflection..)

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

}

Now with local functions especially, but you could always do it by passing a delegate that makes the anonymous type.

So if your goal was to run different logic on the same sources, and be able to combine the results into a single list. Not sure what nuance this is missing to meet the stated goal, but as long as you return a T and pass a delegate to make T, you can return an anonymous type from a function.

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

참고URL : https://stackoverflow.com/questions/10073319/returning-anonymous-type-in-c-sharp

반응형