Как передать анонимные типы в качестве параметров? - PullRequest
120 голосов
/ 08 июля 2011

Как я могу передать анонимные типы в качестве параметров другим функциям?Рассмотрим пример:

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)
    {

    }
}

Другими словами, что я должен использовать вместо ? меток.

Ответы [ 10 ]

153 голосов
/ 08 июля 2011

Я думаю, вы должны создать класс для этого анонимного типа.Это было бы наиболее разумной вещью, по моему мнению.Но если вы действительно не хотите этого, вы можете использовать динамику:

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

Обратите внимание, что это , а не со строгой типизацией, поэтому, если, например, имя изменяется на EmployeeName,не узнает, что есть проблема до времени выполнения.

37 голосов
/ 08 июля 2011

Вы можете сделать это так:

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

    }
}

... но вы не будете делать много с каждым предметом. Вы можете вызвать ToString, но вы не сможете использовать (скажем) Name и Id напрямую.

18 голосов
/ 22 апреля 2009

К сожалению, то, что вы пытаетесь сделать, невозможно.Под капотом переменная запроса вводится как 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>
10 голосов
/ 08 июля 2011

Вы не можете передать анонимный тип не универсальной функции, если тип параметра не равен 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)
    {

    }
}
7 голосов
/ 22 апреля 2009

Обычно вы делаете это с помощью дженериков, например:

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

Затем компилятор должен вывести T при вызове MapEntToObj(query). Не совсем уверен, что вы хотите сделать внутри метода, поэтому я не могу сказать, полезно ли это ... проблема в том, что внутри MapEntToObj вы все еще не можете назвать T - вы также можете:

  • вызов других обобщенных методов с T
  • использовать отражение на T, чтобы делать вещи

но кроме этого, довольно трудно манипулировать анонимными типами - не в последнюю очередь потому, что они неизменны; -p

Еще одна хитрость (когда извлекает данные) - это также передать селектор, т.е. что-то вроде:

Foo<TSource, TValue>(IEnumerable<TSource> source,
        Func<TSource,string> name) {
    foreach(TSource item in source) Console.WriteLine(name(item));
}
...
Foo(query, x=>x.Title);
6 голосов
/ 22 мая 2014

«динамический» также может быть использован для этой цели.

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)
   {

   }
}
6 голосов
/ 08 июля 2011

Вы можете использовать дженерики со следующим приемом (приведение к анонимному типу):

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;
}
2 голосов
/ 08 июня 2015

Вместо передачи анонимного типа передайте список динамического типа:

  1. var dynamicResult = anonymousQueryResult.ToList<dynamic>();
  2. Подпись метода: DoSomething(List<dynamic> _dynamicResult)
  3. Вызов метода: DoSomething(dynamicResult);
  4. сделано.

Спасибо Петру Иванову !

0 голосов
/ 08 июля 2011

Я бы использовал IEnumerable<object> в качестве типа аргумента. Тем не менее, это не большой выигрыш для неизбежного явного приведения. Приветствия

0 голосов
/ 08 июля 2011

Если вы знаете, что ваши результаты реализуют определенный интерфейс, вы можете использовать этот интерфейс как тип данных:

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

    }
}
...