匿名型をパラメーターとして渡す方法は?


143

匿名型をパラメーターとして他の関数に渡すにはどうすればよいですか?この例を考えてみましょう:

var query = from employee in employees select new { Name = employee.Name, Id = employee.Id };
LogEmployees(query);

queryここの変数には強い型はありません。LogEmployees関数を定義してそれを受け入れるにはどうすればよいですか?

public void LogEmployees (? list)
{
    foreach (? item in list)
    {

    }
}

つまり、?マークの代わりに何を使用すればよいでしょうか。


1
より良い別の重複質問そのパラメータを渡すのではなく、データを返すと情報:stackoverflow.com/questions/16823658/...
ロブ教会を

回答:


183

この匿名型のクラスを作成する必要があると思います。それは私の意見では最も賢明なことでしょう。しかし、本当にしたくない場合は、ダイナミクスを使用できます。

public void LogEmployees (IEnumerable<dynamic> list)
{
    foreach (dynamic item in list)
    {
        string name = item.Name;
        int id = item.Id;
    }
}

これは厳密に型指定されていないため、たとえば、NameがEmployeeNameに変更された場合、実行時まで問題があることがわかりません。


dynamic使い方が正しいので正解と確認しました。私は本当に便利になりました。ありがとう:)
Saeed Neamati

1
データの受け渡しが開始されたら、見つけにくいバグを導入しないために、より構造化された方法が推奨される場合があることに同意します(型システムを回避します)。ただし、妥協点を見つけたい場合は、別の方法として、一般的な辞書を渡すだけです。最近のC#辞書初期化子の使用は非常に便利です。
ジョナス

一般的な実装が必要な場合がいくつかあります。ハードタイプを渡すと、コードの肥大化を開始する可能性のある切り替えまたはファクトリ実装を意味します。本当に動的な状況にあり、受信するデータを処理するための少しの反射を気にしない場合、これは完璧です。回答ありがとう@Tim S.
ラリー・スミス

42

あなたはこのようにそれを行うことができます:

public void LogEmployees<T>(List<T> list) // Or IEnumerable<T> list
{
    foreach (T item in list)
    {

    }
}

...しかし、あなたはそれぞれのアイテムで多くをすることはできません。ToStringを呼び出すことはできますが、(たとえば)NameId直接使用することはできません。


2
ただしwhere T : some type、最初の行の終わりでタイプを絞り込むために使用できます。ただし、その時点では、特定のタイプの共通インターフェースを期待する方が、インターフェースを期待する方が理にかなっています。:)
CassOnMars

9
@d_r_w:where T : some typeただし、匿名型はインターフェイスの種類を実装していないため、使用できません...
Jon Skeet

@dlev:これを行うことはできません。foreachは、GetEnumeratorの実装で反復される変数を必要とし、匿名型はそれを保証しません。
CassOnMars

1
@ジョンスキート:良い点、今朝、私の脳は力が不足しています。
CassOnMars 2011

1
@JonSkeet。Tが匿名型の場合、リフレクションを使用してプロパティに引き続きアクセス/設定できると思いますか?誰かが "Select * from"ステートメントを記述し、匿名(または定義済み)クラスを使用して、クエリ結果のどの列を匿名オブジェクトの同じ名前のプロパティにマッピングするかを考えています。
C.テワルト2015

19

残念ながら、あなたがしようとしていることは不可能です。内部的には、クエリ変数は、IEnumerable匿名型として。匿名型名はユーザーコードで表すことができないため、関数の入力パラメーターにすることはできません。

最善の策は、タイプを作成し、それをクエリからの戻り値として使用して、関数に渡すことです。例えば、

struct Data {
  public string ColumnName; 
}

var query = (from name in some.Table
            select new Data { ColumnName = name });
MethodOp(query);
...
MethodOp(IEnumerable<Data> enumerable);

ただし、この場合は単一のフィールドを選択しているだけなので、フィールドを直接選択する方が簡単な場合があります。これにより、クエリIEnumerableがフィールドタイプの型として入力されます。この場合、列名。

var query = (from name in some.Table select name);  // IEnumerable<string>

私の例は1つでしたが、ほとんどの場合それ以上です。作品を通してのあなたの答え(そして今ではかなり明白です)。私はそれを考えるために昼食のための休憩が必要だっただけです;-)
Tony Trembath-Drake


注意点は、適切なクラスを作成すると、Equals動作が変わるということです。つまり、実装する必要があります。(私はこの矛盾を知っていましたが、リファクタリング中にそれを忘れることができました。)
LosManos

11

パラメーターの型がでない限り、非ジェネリック関数に匿名型を渡すことはできませんobject

public void LogEmployees (object obj)
{
    var list = obj as IEnumerable(); 
    if (list == null)
       return;

    foreach (var item in list)
    {

    }
}

匿名型は、メソッド内での短期的な使用を目的としています。

MSDNから- 匿名型

フィールド、プロパティ、イベント、またはメソッドの戻り値の型を匿名型として宣言することはできません。同様に、メソッド、プロパティ、コンストラクター、またはインデクサーの仮パラメーターを匿名型として宣言することはできません。匿名型、または匿名型を含むコレクションをメソッドの引数として渡すには、パラメーターを型オブジェクトとして宣言します。ただし、これを行うと、強い型付けの目的が無効になります。

(強調鉱山)


更新

ジェネリックを使用して、必要なことを実現できます。

public void LogEmployees<T>(IEnumerable<T> list)
{
    foreach (T item in list)
    {

    }
}

4
匿名型(または匿名型のコレクション)をメソッドに渡すことができない場合、LINQ全体が失敗します。できるのは、匿名型のプロパティを使用せずに、メソッドが完全にジェネリックでなければならないということだけです。
Jon Skeet

2
object-またはdynamic; P
マルクGravell

「as」でキャストする場合、リストがnullかどうかを確認する必要があります
Alex

"できる"!= "しなければならない"。使用することobjectは、私の回答のように、メソッドを匿名型でジェネリックにすることと同じではありません。
Jon Skeet、2011

8

通常、これはジェネリックで行います。次に例を示します。

MapEntToObj<T>(IQueryable<T> query) {...}

その後、コンパイラはTを呼び出すときにを推測する必要がありますMapEntToObj(query)。メソッド内で何をしたいのかよくわからないので、これが有用かどうかはわかりません...問題は、内部MapEntToObjでまだ名前を付けることができないTことです。次のいずれかを行うことができます。

  • 他のジェネリックメソッドを呼び出す T
  • リフレクションを使用Tして物事を行う

それ以外は、匿名型を操作するのは非常に困難です-特に、それらは不変であるためです;-p

(データを抽出するときの)もう1つのトリックは、セレクターも渡すことです。

Foo<TSource, TValue>(IEnumerable<TSource> source,
        Func<TSource,string> name) {
    foreach(TSource item in source) Console.WriteLine(name(item));
}
...
Foo(query, x=>x.Title);

1
何か新しいことを学び、匿名型が不変であることを知りませんでした!;)
Annie Lagang、2013年

1
@AnneLagangはそれを生成するため、コンパイラ次第です。VB.NETでは、anon型は変更可能です。
マークグラベル


7

次のトリックでジェネリックを使用できます(匿名型へのキャスト)。

public void LogEmployees<T>(IEnumerable<T> list)
{
    foreach (T item in list)
    {
        var typedItem = Cast(item, new { Name = "", Id = 0 });
        // now you can use typedItem.Name, etc.
    }
}

static T Cast<T>(object obj, T type)
{
    return (T)obj;
}

6

「ダイナミック」もこの目的に使用できます。

var anonymousType = new { Id = 1, Name = "A" };

var anonymousTypes = new[] { new { Id = 1, Name = "A" }, new { Id = 2, Name = "B" };

private void DisplayAnonymousType(dynamic anonymousType)
{
}

private void DisplayAnonymousTypes(IEnumerable<dynamic> anonymousTypes)
{
   foreach (var info in anonymousTypes)
   {

   }
}

1
これが正解です。それはちょうどより多くの愛を必要とする:)
Korayem

2

匿名型を渡す代わりに、動的型のリストを渡します。

  1. var dynamicResult = anonymousQueryResult.ToList<dynamic>();
  2. メソッドの署名: DoSomething(List<dynamic> _dynamicResult)
  3. メソッドを呼び出す: DoSomething(dynamicResult);
  4. 完了しました。

Petar Ivanovに感謝します。


0

結果が特定のインターフェイスを実装していることがわかっている場合は、そのインターフェイスをデータ型として使用できます。

public void LogEmployees<T>(IEnumerable<T> list)
{
    foreach (T item in list)
    {

    }
}

0

IEnumerable<object>引数の型として使用します。ただし、避けられない明示的なキャストに対しては大きな利益ではありません。乾杯

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.