C#で匿名型を返す


99

匿名型を返すクエリがあり、そのクエリはメソッド内にあります。これをどのように書きますか:

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

      return "TheAnonymousType";
    }
}

5
なぜ匿名型を返したいのですか?他の場所でその結果をどのように使用できますか?
Yuck


5
@JSON、またはc#タイプが重要ではない何かを返す場合は
どうでしょう

10
この質問は理にかなっているとは思いません。私は実際にこれを数回行う必要がありました。エンティティフレームワークを使用していて、クエリを1つの関数で実行し、その結果を複数の場所で使用したい場合に、より明白になります。画面に結果を表示し、レポートで同じ結果を使用する必要がある場合、またはExcelにエクスポートする場合、これが頻繁に必要になります。クエリには、UIからのフィルターなどが多数含まれている場合があります。同じクエリを複数の場所で作成したくない場合、または結果に追加したいときに簡単に同期が取れなくなる可能性があります
Kevbo

回答:


94

できません。

あなただけ返すことができobject、例えば、またはオブジェクトのコンテナIEnumerable<object>IList<object>など


51
またはdynamic。これにより、作業が少し簡単になります。
vcsjones 2012

ええと、メソッド内では匿名型しか使用できず、戻り値としては使用できませんか?
フレンチ

2
@frenchie:はい、メンバーの体内のみです。それを返したい場合は、よく知られたタイプにしてください。
abatishchev

11
動的を使用することは解決策ではありません。匿名型のフィールドはパブリックではなく、内部にあります。
ハンスパッサント

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ただし、nugetパッケージをインストールする必要がある場合があります。


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
もちろん、タイプを作成できます。私はこれを避けようとしていました。
フレンチ

最初のリンクは死んでいる、他の場所に転送されたのかどうか、たまたま知りませんか?
レミ2016

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フェーズコンパイラです。最初のフェーズでは、名前空間、クラス階層、メソッドシグネチャなどをチェックするだけです。メソッドの本体は、第2フェーズでのみコンパイルされます。

匿名型は、メソッド本体がコンパイルされるまで決定されません。

そのため、コンパイラーは最初のフェーズでメソッドの戻りタイプを判別する方法がありません。

これが、匿名型を戻り値の型として使用できない理由です。

他の人が.net 4.0またはおろし金を使用している場合に示唆しているように、を使用できますDynamic

もし私があなただったら、おそらく型を作成し、メソッドからその型を返します。そうすれば、コードを保守し、より読みやすくする将来のプログラマーにとって簡単になります。


8

3つのオプション:

オプション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を使用しても、匿名型を返すことはできませんが、タプル型のサポートがあるため、tupleSystem.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;}
}

独自のクラスを作成し、それをクエリすることが、私が知っている最善の解決策です。匿名型の戻り値は、認識されないため、別のメソッドで使用できないことはわかっていますが、同じように使用できます。方法。以前はそれらをIQueryableor として返していました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

もう1つのオプションは、オートマッパーを使用することです。長いパブリックプロパティが一致する場合、匿名で返されたオブジェクトから任意の型に変換します。重要なポイントは、オブジェクトを返す、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

特にローカル関数を使用するようになりましたが、匿名型を作成するデリゲートを渡すことでいつでも実行できます。

したがって、同じソースで異なるロジックを実行し、結果を1つのリストに結合できることが目標である場合。指定された目標を達成するためにこれがどのニュアンスに欠けているかはわかりませんが、aを返しT、デリゲートをmakeに渡せ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を使用して)ログファイルにシリアル化することです。また、設定を処理する複雑なロジックをカプセル化し、それを処理に必要なものから切り離したいと考えています。すべて、1回限りの使用のために存在する名前付きクラスを作成する必要はありません。

匿名型を作成するローカル関数を使用してこれを解決する方法を見てみましょう。

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.