¿Cómo pasar tipos anónimos como parámetros?

143

¿Cómo puedo pasar tipos anónimos como parámetros a otras funciones? Considere este ejemplo:

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

La variable queryaquí no tiene un tipo fuerte. ¿Cómo debo definir mi LogEmployeesfunción para aceptarla?

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

    }
}

En otras palabras, ¿qué debo usar en lugar de ?marcas?

Saeed Neamati
fuente
1
Mejor pregunta duplicada diferente que se ocupa de pasar parámetros en lugar de devolver datos: stackoverflow.com/questions/16823658/…
Rob Church

Respuestas:

183

Creo que deberías hacer una clase para este tipo anónimo. Eso sería lo más sensato que hacer en mi opinión. Pero si realmente no quieres, puedes usar la dinámica:

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

Tenga en cuenta que esto no está fuertemente tipeado, por lo que si, por ejemplo, Nombre cambia a EmployeeName, no sabrá que hay un problema hasta el tiempo de ejecución.

Tim S.
fuente
Verifiqué esto como respuesta correcta, debido al dynamicuso. Realmente me fue útil. Gracias :)
Saeed Neamati
1
Estoy de acuerdo en que una vez que los datos comiencen a transmitirse, una forma más estructurada puede / debería preferirse normalmente para no introducir errores difíciles de encontrar (está esquivando el sistema de tipos). Sin embargo, si desea encontrar un compromiso, otra forma es simplemente pasar un Diccionario genérico. Los inicializadores de diccionario C # son bastante convenientes para usar en estos días.
Jonas
Hay algunos casos en los que desea una implementación genérica, y pasar tipos difíciles significa posiblemente un cambio o una implementación de fábrica que comienza a inflar el código. Si tiene una situación verdaderamente dinámica y no le importa reflexionar un poco para tratar con los datos que está recibiendo, entonces esto es perfecto. Gracias por la respuesta @Tim S.
Larry Smith
42

Puedes hacerlo así:

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

    }
}

... pero no podrás hacer mucho con cada artículo. Puede llamar a ToString, pero no podrá usar (por ejemplo) Namey Iddirectamente.

Jon Skeet
fuente
2
Excepto que puede usar where T : some typeal final de la primera línea para reducir el tipo. En ese punto, sin embargo, esperar un cierto tipo de interfaz común tendría más sentido esperar una interfaz. :)
CassOnMars
9
@d_r_w: Sin where T : some typeembargo, no se puede usar con tipos anónimos, ya que no implementan ningún tipo de interfaz ...
Jon Skeet
@dlev: No puede hacer eso, foreach requiere que la variable itere al implementar GetEnumerator, y los tipos anónimos no lo garantizan.
CassOnMars
1
@ Jon Skeet: buen punto, mi cerebro tiene poca potencia esta mañana.
CassOnMars
1
@JonSkeet. Supongo que podría usar la reflexión para acceder / configurar las propiedades si T es un tipo anónimo, ¿verdad? Estoy pensando en un caso en el que alguien escribe una declaración "Seleccionar * de" y usa una clase anónima (o definida) para definir qué columnas del mapa de resultados de la consulta se asignan a las mismas propiedades nombradas en su objeto anónimo.
C. Tewalt
19

Desafortunadamente, lo que estás tratando de hacer es imposible. Bajo el capó, la variable de consulta se escribe para ser IEnumerablede tipo anónimo. Los nombres de tipos anónimos no se pueden representar en el código de usuario, por lo tanto, no hay forma de convertirlos en un parámetro de entrada para una función.

Su mejor opción es crear un tipo y usarlo como el retorno de la consulta y luego pasarlo a la función. Por ejemplo,

struct Data {
  public string ColumnName; 
}

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

Sin embargo, en este caso, solo está seleccionando un solo campo, por lo que puede ser más fácil seleccionar el campo directamente. Esto hará que la consulta se escriba como IEnumerabledel tipo de campo. En este caso, nombre de columna.

var query = (from name in some.Table select name);  // IEnumerable<string>
JaredPar
fuente
Mi ejemplo fue uno, pero la mayoría de las veces es más. Su respuesta a través de trabajos (y bastante obvio ahora). Aunque solo necesitaba un descanso para almorzar ;-)
Tony Trembath-Drake
FYI: se fusionó de stackoverflow.com/questions/775387/…
Shog9
Una advertencia es que cuando crea una clase adecuada, Equalscambia el comportamiento. Es decir, tienes que implementarlo. (Conocía esta discrepancia, pero aún así me olvidé de ella durante una refactorización).
LosManos
11

No puede pasar un tipo anónimo a una función no genérica, a menos que el tipo de parámetro sea object.

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

    foreach (var item in list)
    {

    }
}

Los tipos anónimos están destinados al uso a corto plazo dentro de un método.

De MSDN - Tipos anónimos :

No puede declarar que un campo, una propiedad, un evento o el tipo de retorno de un método tenga un tipo anónimo. Del mismo modo, no puede declarar que un parámetro formal de un método, propiedad, constructor o indexador tenga un tipo anónimo. Para pasar un tipo anónimo, o una colección que contiene tipos anónimos, como argumento para un método, puede declarar el parámetro como objeto de tipo . Sin embargo, hacer esto anula el propósito de escribir con fuerza.

(énfasis mío)


Actualizar

Puedes usar genéricos para lograr lo que quieres:

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

    }
}
Oded
fuente
44
Si no puede pasar los tipos anónimos (o colecciones de un tipo anónimo) a los métodos, la totalidad de LINQ fallaría. Puede, es solo que el método tiene que ser completamente genérico, no usar las propiedades del tipo anónimo.
Jon Skeet
2
re object- or dynamic; p
Marc Gravell
Si está lanzando con "como", debe verificar si la lista es nula
Alex
"can"! = "have to". Usar objectno es lo mismo que hacer un método genérico en el tipo anónimo, según mi respuesta.
Jon Skeet
8

Normalmente, haces esto con genéricos, por ejemplo:

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

El compilador debe inferir Tcuándo llama MapEntToObj(query). No estoy muy seguro de lo que quiere hacer dentro del método, por lo que no puedo decir si esto es útil ... el problema es que dentro de MapEntToObjusted todavía no puede nombrar el T- usted puede:

  • llamar a otros métodos genéricos con T
  • usar la reflexión Tpara hacer cosas

pero aparte de eso, es bastante difícil manipular tipos anónimos, sobre todo porque son inmutables ;-p

Otro truco (al extraer datos) es también pasar un selector, es decir, algo como:

Foo<TSource, TValue>(IEnumerable<TSource> source,
        Func<TSource,string> name) {
    foreach(TSource item in source) Console.WriteLine(name(item));
}
...
Foo(query, x=>x.Title);
Marc Gravell
fuente
1
¡Aprendí algo nuevo, no sabía que los tipos anónimos son inmutables! ;)
Annie Lagang
1
@AnneLagang que realmente depende del compilador, ya que los genera. En VB.NET, los anon-types pueden ser mutables.
Marc Gravell
1
FYI: se fusionó de stackoverflow.com/questions/775387/…
Shog9
7

Puedes usar genéricos con el siguiente truco (conversión a tipo anónimo):

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;
}
Stanislav Basovník
fuente
6

"dinámico" también se puede utilizar para este propósito.

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

   }
}
Dinesh Kumar P
fuente
1
¡Esta es la respuesta correcta! Solo necesita más amor :)
Korayem
2

En lugar de pasar un tipo anónimo, pase una Lista de un tipo dinámico:

  1. var dynamicResult = anonymousQueryResult.ToList<dynamic>();
  2. Firma del método: DoSomething(List<dynamic> _dynamicResult)
  3. Método de llamada: DoSomething(dynamicResult);
  4. hecho.

Gracias a Petar Ivanov !

útilBee
fuente
0

Si sabe que sus resultados implementan una determinada interfaz, puede usar la interfaz como tipo de datos:

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

    }
}
Alex
fuente
0

Lo usaría IEnumerable<object>como tipo para el argumento. Sin embargo, no es una gran ganancia para el elenco explícito inevitable. Salud

Mario Vernari
fuente