Pregunta

Considera esto:

var me = new { FirstName = "John", LastName = "Smith" };

Esto está bien, ya que podemos hacer esto:

Console.WriteLine("{0} {1}", me.FirstName, me.LastName);

Sin embargo, no podemos hacer esto:

public T GetMe()
{
    return new { FirstName = "John", LastName = "Smith" };
}

porque no sabemos el tipo de T.

Podríamos hacer esto:

public object GetMe()
{
    return new { FirstName = "John", LastName = "Smith" };
}

pero luego tendríamos que inspeccionar las propiedades del objeto usando la reflexión para acceder a ellas:

var p = new Prog();
object o = p.GetMe();
Type t = o.GetType();
foreach (var prop in t.GetProperties())
{
    Console.WriteLine(prop.Name + ": " + prop.GetValue(o, null));
}

Sin embargo, ¿qué pasa si pudiéramos nombrar un tipo anónimo como lo definimos? Por supuesto, ya no sería anónimo, sin embargo, sería más sucinto y fácil de mantener que una definición de clase normal.

Considera esto:

public Person GetMe()
{
    return new public class Person { FirstName = "John", LastName = "Smith" };
}

El beneficio es que sería posible devolver el resultado de una consulta Linq complicada de un método sin tener que definir la clase explícitamente.

Considere esta consulta Linq relativamente compleja:

List<int> list = new List<int>();
var query = from number in list
            select
                new
                    {
                        Number = number,
                        Square = number*number,
                        Absolute = Math.Abs(number),
                        Range = Enumerable.Range(0, number)
                    };

En lugar de definir una clase así:

public class MyNumbers
{
    public int Number { get; set; }
    public int Square { get; set; }
    public int Absolute { get; set; }
    public IEnumerable<int> Range { get; set; }
}

para devolver la variable de consulta de un método, podríamos simplemente hacer esto:

List<int> list = new List<int>();
return from number in list
            select new public class MyNumbers
                    {
                        Number = number,
                        Square = number*number,
                        Absolute = Math.Abs(number),
                        Range = Enumerable.Range(0, number)
                    };
¿Fue útil?

Solución

En realidad, hay un " hack " que puede hacer para recuperar un tipo anónimo de un método. Considera esto:

public object MyMethod()
    {
        var myNewObject = new
        {
            stringProperty = "Hello, World!",
            intProperty = 1337,
            boolProperty = false
        };

        return myNewObject;
    }

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

Ahora puede hacer esto:

var obj = MyMethod();
var myNewObj = Cast(obj, new { stringProperty = "", intProperty = 0, boolProperty = false });

myNewObj ahora será un objeto del mismo tipo que el tipo anónimo.

Otros consejos

La función de idioma que necesita es:

public var GetMe()
{
    return new { FirstName = "John", LastName = "Smith" };
}

Es decir, var sería válido como un tipo de retorno de método, y el compilador inferiría el tipo real de lo que se devuelva. Debería hacer esto en el sitio de la llamada:

var me = GetMe();

Dos tipos anónimos con miembros del mismo tipo serían del mismo tipo, por lo que si escribiera otras funciones que devuelvan el mismo patrón, tendrían el mismo tipo. Para cualquier tipo A y B donde B tiene un subconjunto de los miembros de A, entonces A es compatible con la asignación de B (B es como una clase base de A). Si escribiste:

public var GetMeFrom(var names)
{
    return new { FirstName = names["First"], LastName = names["Last"] };
}

El compilador definiría efectivamente esto como un método genérico con dos parámetros de tipo, T1 es el tipo de nombres y T2 es el tipo devuelto por el indexador en T1 que acepta una cadena. T1 estaría restringido de modo que debe tener un indexador que acepte una cadena. Y en el sitio de la llamada, simplemente pasaría cualquier cosa que tuviera un indexador que aceptara una cadena y devolviera cualquier tipo que desee , y eso determinaría el tipo de FirstName y Apellido en el tipo devuelto por GetMeFrom .

Entonces, la inferencia de tipos resolvería todo esto para usted, capturando automáticamente cualquier restricción de tipo que se pueda descubrir en el código.

En mi humilde opinión, el problema raíz no tiene nada que ver con los tipos anónimos, sino que declarar una clase es demasiado detallado.

Opción 1:

Si pudiera declarar una clase como esta:

public class MyClass
{ properties={ int Number, int Square, int Absolute, IEnumerable<int> Range } }

u otra forma similarmente rápida (como el ejemplo de tupla), entonces no sentiría la necesidad de hacer cosas extrañas con tipos anónimos solo para guardar algo de código.

Cuando llegue el 'compilador como servicio' en C # 5, esperamos que hagan un buen trabajo al integrarlo y podamos usar la metaprogramación para resolver este tipo de problemas de manera limpia. ¡Festeja como si fuera 1958!

Opción 2:

Alternativamente, en C # 4, puede pasar un tipo anónimo como dynamic y evitar todo el casting. Por supuesto, esto lo abre a errores de tiempo de ejecución si cambia el nombre de una variable, etc.

Opción 3:

Si C # implementara genéricos de la misma manera que C ++, entonces podría pasar el tipo anónimo a un método, y siempre que tuviera los miembros correctos, simplemente se compilaría. Obtendrá todos los beneficios de la seguridad de tipo estático, y ninguno de los inconvenientes. ¡Cada vez que tengo que escribir donde T: ISomething en C # me molesta que no hayan hecho esto!

Lo que está describiendo (tipos anónimos denominados) son básicamente "tipos de tupla".

Creo que serían una buena adición a C #.

Si estuviera diseñando una función de este tipo para C #, la expondría utilizando una sintaxis como esta:

tuple<int x, int y>

para que puedas hacer:

public tuple<int x, int y> GetStuff()
{
}

Luego cambiaría la definición de tipos anónimos, para que:

new { x = 2, y = 2}

tenía tupla < int x, int y > como su tipo, en lugar de un tipo anónimo.

Hacer que esto funcione con el CLR actual es un poco complicado, porque una vez que puede nombrar un tipo anónimo en firmas públicas, debe poder unificarlos en ensamblados compilados por separado. Se puede lograr incrustando un "constructor de módulos". dentro de cualquier ensamblaje que use un tipo de tupla. Consulte esta publicación para ver un ejemplo.

El único inconveniente de ese enfoque es que no respeta el '' perezoso '' de CLR modelo para generación tipográfica. Eso significa que los ensamblajes que usan muchos tipos distintos de tuplas pueden experimentar tipos de carga ligeramente más lentos. Un mejor enfoque sería agregar soporte para tipos de tupla directamente al CLR.

Pero, además de cambiar el CLR, creo que el enfoque del constructor de módulos es la mejor manera de hacer algo como esto.

Me encantaría esta función, muchas veces he querido esto.

Un buen ejemplo es el procesamiento de XML. Los analiza para recuperar un objeto, pero luego debe hacer una versión concreta del objeto para enviarlo de nuevo a la persona que llama. Muchas veces obtienes XML que cambia considerablemente y requiere que hagas muchas clases para manejarlo. ¿No sería maravilloso si pudieras construir el objeto usando LinqToXml como var, y luego devolverlo?

Creo que este sería un buen compilador de magia para tuplas:

Crear una tupla:

(int, string, Person) tuple = (8, "hello", new Person());

equivalente a:

Tuple<int,string,Person> tuple = new Tuple<int,string,Person>(8 ,"hello", new Person());

En una función:

public (int, string, Person) GetTuple(){
    return ...
}

Obteniendo valores:

int number = tuple[1];
string text = tuple[2];
Person person = tuple[3];

¿Podría crear una interfaz con las propiedades Nombre y Apellido y usarla?

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top