Как передавать анонимные типы в качестве параметров?

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

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

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

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

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

Ответ 2

Вы можете сделать это следующим образом:

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

    }
}

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

Ответ 3

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

Ответ 4

Вы не можете передавать анонимный тип в не общую функцию, если только тип параметра object.

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

    foreach (var item in list)
    {

    }
}

Анонимные типы предназначены для краткосрочного использования в рамках метода.

От MSDN - Анонимные типы:

Вы не можете объявить поле, свойство, событие или возвращаемый тип метода как анонимный. Точно так же вы не можете объявить формальный параметр метода, свойства, конструктора или индексатора как анонимного типа. Чтобы передать анонимный тип или коллекцию, содержащую анонимные типы, в качестве аргумента для метода, вы можете объявить этот параметр как объект типа. Тем не менее, это означает, что цель состоит в сильной типизации.

(акцент мой)


Update

Вы можете использовать дженерики для достижения желаемого результата:

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

    }
}

Ответ 5

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

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

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

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;
}

Ответ 7

"dynamic" также может использоваться для этой цели.

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

   }
}

Ответ 8

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

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

Благодаря Петр Иванов!

Ответ 9

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

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

    }
}

Ответ 10

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