Pergunta

Considere isto:

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

Isso é bom, pois podemos fazer isso:

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

No entanto, não podemos fazer isso:

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

Porque não sabemos o tipo de T.

Poderíamos fazer isso:

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

Mas então teríamos que inspecionar as propriedades do objeto usando a reflexão para acessá -las:

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

No entanto, e se pudéssemos nomear um tipo anônimo enquanto o definimos? É claro que não seria mais anônimo, no entanto, seria mais sucinto e sustentável do que uma definição normal de classe.

Considere isto:

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

O benefício é que seria possível devolver o resultado de uma consulta LINQ complicada de um método sem precisar definir explicitamente a classe.

Considere esta consulta LINQ relativamente complexa:

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

Em vez de definir uma aula como sim:

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 retornar a variável de consulta de um método, poderíamos fazer isso:

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)
                    };
Foi útil?

Solução

Na verdade, há um "hack" que você pode fazer para obter um tipo anônimo de volta de um método. Considere isto:

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

Agora você pode fazer isso:

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

O MyNewobj agora será um objeto do mesmo tipo que o tipo anônimo.

Outras dicas

O recurso de idioma que você precisa é:

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

Aquilo é, var seria válido como um tipo de retorno de método, e o compilador inferiria o tipo real de qualquer que seja retornado. Você teria que fazer isso no site de chamadas:

var me = GetMe();

Quaisquer dois tipos anônimos com membros do mesmo tipo seriam o mesmo tipo; portanto, se você escrevesse outras funções retornando o mesmo padrão, eles teriam o mesmo tipo. Para qualquer tipo A e B, onde B possui um subconjunto dos membros de A, a A é compatível com a tarefa com B (B é como uma classe base de A). Se você escreveu:

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

O compilador definiria efetivamente isso como um método genérico com dois parâmetros de tipo, T1 sendo o tipo de nomes e T2 sendo o tipo devolvido pelo indexador T1 que aceita uma string. T1 seria restringido para que ele tenha um indexador que aceite uma string. E no site de chamadas, você apenas passaria qualquer coisa que tivesse um indexador que aceitasse uma string e retornasse Qualquer tipo que você goste, e isso determinaria o tipo de FirstName e LastName no tipo retornado por GetMeFrom.

Portanto, o tipo de inferência descobriria tudo isso para você, capturando automaticamente quaisquer restrições de tipo que sejam descobertas do código.

IMHO O problema raiz não tem nada a ver com tipos anônimos, mas que declarar uma classe é muito detalhada.

Opção 1:

Se você pudesse declarar uma aula como esta:

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

Ou alguma outra maneira igualmente rápida (como o exemplo da tupla), você não sentiria a necessidade de fazer coisas hacky com tipos anônimos apenas para economizar algum código.

Quando o 'Compiler como serviço' chega ao C#5, espero que eles façam um bom trabalho ao integrá -lo e possamos usar o metaprogramação para resolver esses tipos de problemas de maneira limpa. Festa como se fosse 1958!

Opção 2:

Como alternativa, em C#4, você pode simplesmente passar um tipo anônimo como dynamic E evite todo o elenco. É claro que isso abre você para erros de tempo de execução se você renunciar a uma variável, etc.

Opção 3:

Se o C# implementaria genéricos da mesma maneira que o C ++, você poderá passar o tipo anônimo em um método e, desde que tivesse os membros certos, ele apenas compilaria. Você obteria todos os benefícios da segurança do tipo estático e nenhuma das desvantagens. Cada vez que tenho que digitar where T : ISomething Em C#, fico irritado por eles não fizeram isso!

O que você está descrevendo (denominados tipos anônimos) são basicamente "tipos de tupla".

Eu acho que eles seriam uma boa adição para C#.

Se eu estivesse projetando esse recurso para C#, eu o exporia usando sintaxe como esta:

tuple<int x, int y>

para que você pudesse fazer:

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

Eu mudaria então a definição de tipos anônimos, para que:

new { x = 2, y = 2}

teve tuple<int x, int y> Como é o tipo, em vez de um tipo anônimo.

Fazer isso funcionar com o CLR atual é um pouco complicado, porque, uma vez que você possa nomear um tipo anônimo de assinaturas públicas, você precisa unificá -las em montagens compiladas separadamente. Pode ser realizado incorporando um "construtor de módulos" dentro de qualquer montagem que use um tipo de tupla. Ver esta postagem Por exemplo.

A única desvantagem dessa abordagem é que ela não respeita o modelo "preguiçoso" do CLR para a geração de tipos. Isso significa que os conjuntos que usam muitos tipos distintos de tupla podem sofrer tipos de carga um pouco mais lentos. Uma abordagem melhor seria adicionar suporte para tipos de tupla diretamente ao CLR.

Mas, além de mudar o CLR, acho que a abordagem do construtor do módulo é a melhor maneira de fazer algo assim.

Eu adoraria esse recurso, houve muitas vezes que eu queria isso.

Um bom exemplo é processar XML. Você analisa eles de volta um objeto, mas precisa fazer uma versão concreta do objeto para enviar de volta para um chamador. Muitas vezes você obtém XML que muda consideravelmente e exige que você faça muitas classes para lidar com isso. Não seria maravilhoso se você pudesse construir o objeto usando o Linqtoxml como um VAR, então apenas devolva isso?

Eu acho que essa seria uma boa mágica do compilador para tuplas:

Criando uma 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());

Em uma função:

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

Obtendo valores:

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

Você poderia criar uma interface com o primeiro nome da Propriedades e o último nome e usá -lo?

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top