C #에서 익명 형식 반환


100

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

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

      return "TheAnonymousType";
    }
}

5
익명 유형을 반환하려는 이유는 무엇입니까? 그 결과를 다른 곳에서 어떻게 사용할 수 있습니까?
Yuck


5
@Yuck 당신이 json 또는 c # 유형이 중요하지 않은 것을 반환한다면 어떨까요
aw04

10
이 질문은 이유가 없다고 생각합니다. 나는 실제로 이것을 여러 번해야했다. 엔터티 프레임 워크를 사용할 때 더 분명하고 하나의 함수에서 쿼리를 수행하고 여러 위치에서 결과를 사용하려고합니다. 화면에 결과를 표시 한 다음 보고서에서 동일한 결과를 사용해야하거나 Excel로 내보낼 때 자주 필요합니다. 쿼리에는 UI의 많은 필터 등이 포함될 수 있습니다. 여러 위치에서 동일한 쿼리를 작성하고 싶지 않거나 결과에 추가하고 싶을 때 쉽게 동기화되지 않을 수 있습니다
Kevbo

답변:


94

당신은 할 수 없습니다.

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


51
또는 dynamic. 따라서 작업하기가 조금 더 쉬워졌습니다.
vcsjones 2012

아 좋아, 그래서 당신은 반환 값이 아닌 메서드 내에서 익명 유형을 사용할 수 있습니까?
frenchie 2012

2
@frenchie : 네, 회원의 몸 안에 만 있습니다. 반환하려면 잘 알려진 유형으로 만드십시오.
abatishchev 2012

11
동적을 사용 하는 것은 해결책이 아니며 익명 유형의 필드는 공용이 아니며 내부입니다.
Hans Passant 2012

7
@HansPassant 호출자가 동일한 어셈블리에 있다고 가정하면 여전히 (다소) 유용합니다. 가치가있는 필드는 공용이며 유형은 내부입니다. 나는 일반적으로 어차피 익명 유형을 반환해서는 안되는 캠프에 있습니다.
vcsjones 2012

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.

다음은 주제에 대해 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


8
물론 유형을 만들 수 있습니다. 나는 이것을 피하고 싶었다.
frenchie 2012

첫 번째 링크가 죽었습니다. 다른 곳으로 전송되었는지 알 수 없습니까?
Rémi

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

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

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

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

.net 4.0 또는 grater를 사용하는 경우 다른 사람들이 제안했듯이 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

또 다른 옵션은 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; }
        }
    }

}

0

이제 특히 로컬 함수를 사용하지만 익명 형식을 만드는 대리자를 전달하여 항상 수행 할 수 있습니다.

따라서 목표가 동일한 소스에서 다른 로직을 실행하고 결과를 단일 목록으로 결합하는 것이 었습니다. 명시된 목표를 달성하기 위해 이것이 누락 된 뉘앙스가 무엇인지 확실하지 않지만 a를 반환하고 Tmake에 대리자를 전달하는 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자체 "표현식" 내부 와 내부에서 캡슐화했습니다 . 이것은 OP가 원했던 것과 정확히 일치하지 않을 수도 있지만 현재 최신 C #에서 가능한 익명 유형을 사용하는 경량 접근 방식입니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.