匿名型をパラメーターとして他の関数に渡すにはどうすればよいですか?この例を考えてみましょう:
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)
{
}
}
言い換えれば、?
マークの代わりに何を使用する必要があります。
この匿名型のクラスを作成する必要があると思います。私の意見では、それが最も賢明なことです。しかし、本当にしたくない場合は、ダイナミクスを使用できます。
public void LogEmployees (IEnumerable<dynamic> list)
{
foreach (dynamic item in list)
{
string name = item.Name;
int id = item.Id;
}
}
これはnot厳密に型指定されているため、たとえば、NameがEmployeeNameに変更された場合、実行時まで問題があることはわかりません。
次のようにできます:
public void LogEmployees<T>(List<T> list) // Or IEnumerable<T> list
{
foreach (T item in list)
{
}
}
...しかし、各アイテムで多くのことを行うことはできません。 ToStringを呼び出すことはできますが、(たとえば)Name
およびId
を直接使用することはできません。
残念ながら、あなたがやろうとしていることは不可能です。内部では、クエリ変数は匿名型のIEnumerableとして入力されます。匿名型の名前はユーザーコードでは表現できないため、関数への入力パラメーターにする方法はありません。
最善の方法は、型を作成し、それをクエリからの戻り値として使用し、それを関数に渡すことです。例えば、
struct Data {
public string ColumnName;
}
var query = (from name in some.Table
select new Data { ColumnName = name });
MethodOp(query);
...
MethodOp(IEnumerable<Data> enumerable);
ただし、この場合、選択するフィールドは1つだけなので、フィールドを直接選択する方が簡単な場合があります。これにより、フィールドタイプのIEnumerableとしてクエリが入力されます。この場合、列名。
var query = (from name in some.Table select name); // IEnumerable<string>
パラメータタイプが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)
{
}
}
通常、これはジェネリックで行います。たとえば、次のとおりです。
MapEntToObj<T>(IQueryable<T> query) {...}
コンパイラは、MapEntToObj(query)
を呼び出すときにT
を推測する必要があります。メソッド内で何をしたいのかよくわからないので、これが便利かどうかわかりません...問題はMapEntToObj
内でT
に名前を付けることができないことです-あなたは次のいずれかができます:
T
を使用して他の汎用メソッドを呼び出しますT
でリフレクションを使用して物事を行うしかし、それ以外では、匿名型を操作するのは非常に困難です-特に不変であるためです;-p
別のトリック(extracting dataの場合)は、セレクターも渡すことです-つまり、次のようなものです:
Foo<TSource, TValue>(IEnumerable<TSource> source,
Func<TSource,string> name) {
foreach(TSource item in source) Console.WriteLine(name(item));
}
...
Foo(query, x=>x.Title);
次のトリック(ジェネリック型へのキャスト)でジェネリックを使用できます。
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;
}
「動的」もこの目的に使用できます。
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)
{
}
}
匿名タイプを渡す代わりに、動的タイプのリストを渡します。
var dynamicResult = anonymousQueryResult.ToList<dynamic>();
DoSomething(List<dynamic> _dynamicResult)
DoSomething(dynamicResult);
Petar Ivanov に感謝!
結果が特定のインターフェイスを実装していることがわかっている場合、そのインターフェイスをデータ型として使用できます。
public void LogEmployees<T>(IEnumerable<T> list)
{
foreach (T item in list)
{
}
}
引数の型としてIEnumerable<object>
を使用します。しかし、避けられない明示的なキャストにとっては大きな利益ではありません。乾杯