Domanda

Considera questo:

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

Questo va bene come possiamo quindi fare questo:

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

Tuttavia non possiamo farlo:

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

perché non conosciamo il tipo di T.

Potremmo farlo:

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

ma poi dovremmo ispezionare le proprietà dell'oggetto usando la riflessione per accedervi:

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

Tuttavia, se potessimo nominare un tipo anonimo mentre lo definiamo? Ovviamente non sarebbe più anonimo, tuttavia sarebbe più sintetico e mantenibile di una normale definizione di classe.

Considera questo:

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

Il vantaggio sarebbe che sarebbe quindi possibile restituire il risultato di una query Linq complicata da un metodo senza dover definire esplicitamente la classe.

Considera questa query Linq relativamente complessa:

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

Invece di definire una classe in questo modo:

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

per restituire la variabile di query da un metodo potremmo invece semplicemente fare questo:

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)
                    };
È stato utile?

Soluzione

In realtà, c'è un " hack " che puoi fare per recuperare un tipo anonimo da un metodo. Considera questo:

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

Ora puoi farlo:

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

myNewObj ora sarà un oggetto dello stesso tipo del tipo anonimo.

Altri suggerimenti

La funzionalità della lingua che ti serve è:

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

Cioè, var sarebbe valido come tipo di restituzione del metodo e il compilatore dedurrebbe il tipo effettivo da qualunque cosa venga restituita. Dovresti quindi farlo nel sito di chiamata:

var me = GetMe();

Qualsiasi due tipi anonimi con membri dello stesso tipo sarebbero dello stesso tipo, quindi se scrivessi altre funzioni che restituiscono lo stesso modello, avrebbero lo stesso tipo. Per tutti i tipi A e B in cui B ha un sottoinsieme dei membri di A, allora A è compatibile con B con l'assegnazione (B è come una classe base di A). Se hai scritto:

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

Il compilatore lo definirebbe effettivamente come un metodo generico con due parametri di tipo, T1 è il tipo di nomi e T2 è il tipo restituito dall'indicizzatore su T1 che accetta una stringa. T1 sarebbe vincolato in modo che debba avere un indicizzatore che accetti una stringa. E sul sito di chiamata passeresti qualsiasi cosa che avesse un indicizzatore che accettasse una stringa e restituisse qualsiasi tipo ti piaccia , e questo determinerebbe il tipo di FirstName e Cognome nel tipo restituito da GetMeFrom .

Quindi l'inferenza del tipo lo capirebbe per te, catturando automaticamente qualsiasi tipo di vincoli rilevabili dal codice.

IMHO il problema alla radice non ha nulla a che fare con i tipi anonimi, ma che dichiarare una classe è troppo prolisso.

Opzione 1:

Se potessi dichiarare una classe come questa:

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

o in qualche altro modo altrettanto rapido (come nell'esempio della tupla), allora non sentiresti la necessità di fare cose confuse con tipi anonimi solo per salvare un po 'di codice.

Quando "compilatore come servizio" arriverà in C # 5, si spera che facciano un buon lavoro di integrazione e saremo in grado di usare la metaprogrammazione per risolvere in modo pulito questi tipi di problemi. Festeggia come se fosse il 1958!

Opzione 2:

In alternativa, in C # 4, potresti semplicemente passare un tipo anonimo come dinamico ed evitare tutto il casting. Naturalmente questo ti apre agli errori di runtime se rinomini una variabile, ecc.

Opzione 3:

Se C # implementasse i generici allo stesso modo di C ++, allora potresti passare il tipo anonimo in un metodo, e fintanto che avesse i membri giusti, sarebbe semplicemente compilato. Otterresti tutti i vantaggi della sicurezza di tipo statico e nessuno degli aspetti negativi. Ogni volta che devo digitare dove T: ISomething in C # mi annoio che non lo abbiano fatto!

Quello che stai descrivendo (denominati tipi anonimi) sono fondamentalmente "tipi di tuple".

Penso che sarebbero una bella aggiunta a C #.

Se stavo progettando una tale funzione per C #, la esporrei usando una sintassi come questa:

tuple<int x, int y>

in modo che tu possa fare:

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

Vorrei quindi modificare la definizione di tipi anonimi, in modo che:

new { x = 2, y = 2}

aveva tuple < int x, int y > come tipo, piuttosto che un tipo anonimo.

Far funzionare questo con l'attuale CLR è un po 'complicato, perché una volta che puoi nominare un tipo anonimo nelle firme pubbliche devi essere in grado di unificarle tra assiemi compilati separatamente. Può essere realizzato incorporando un "costruttore di moduli" all'interno di qualsiasi assieme che utilizza un tipo di tupla. Vedi questo post per un esempio.

L'unico aspetto negativo di questo approccio è che non rispetta il "pigro" del CLR modello per la generazione del tipo. Ciò significa che gli assiemi che utilizzano molti tipi distinti di tuple potrebbero presentare tipi di carico leggermente più lenti. Un approccio migliore sarebbe quello di aggiungere il supporto per i tipi di tupla direttamente al CLR.

Ma, a parte cambiare il CLR, penso che l'approccio del costruttore del modulo sia il modo migliore di fare qualcosa del genere.

Mi piacerebbe questa funzionalità, ci sono state molte volte che ho voluto questo.

Un buon esempio è l'elaborazione di XML. Li analizzi per recuperare un oggetto, ma devi creare una versione concreta dell'oggetto da inviare a un chiamante. Molte volte si ottiene XML che cambia in modo considerevole e richiede di creare molte classi per gestirlo. Non sarebbe meraviglioso se si potesse semplicemente costruire l'oggetto usando LinqToXml come var, quindi restituirlo?

Penso che sarebbe una bella magia da compilatore per le tuple:

Creazione di 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());

In una funzione:

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

Ottenere valori:

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

Potresti creare un'interfaccia con le proprietà FirstName e LastName e usarla?

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top