Domanda

Sto creando una funzione in cui devo passare un oggetto in modo che possa essere modificato dalla funzione.Qual è la differenza tra:

public void myFunction(ref MyClass someClass)

E

public void myFunction(out MyClass someClass)

Quale dovrei usare e perché?

È stato utile?

Soluzione

ref dice al compilatore che l'oggetto viene inizializzato prima di entrare in funzione, mentre out indica al compilatore che l'oggetto verrà inizializzato all'interno della funzione.

Così, mentre ref è a due vie, out è fuori-solo.

Altri suggerimenti

Il modificatore ref significa che:

  1. Il valore è già impostato e
  2. Il metodo in grado di leggere e modificare.

Il modificatore out significa che:

  1. Il valore non è impostato e non può essere letto con il metodo fino a è impostato.
  2. Il metodo deve impostarla prima di tornare.

Diciamo Dom si presenta al cubicolo di Peter circa il promemoria per il TPS relazioni.

Se Dom fosse un argomento ref, avrebbe avuto una copia stampata del memo.

Se Dom fosse un argomento fuori, che avrebbe fatto Peter stampare una nuova copia del memo per lui a prendere con sé.

Ho intenzione di provare la mia mano a una spiegazione:

Credo che si comprende come i tipi di valore funziona bene? I tipi di valore sono (int, long, struct, ecc). Quando li si invia a una funzione senza un comando Rif copia il dati . Tutto ciò che fate a tali dati nella funzione riguarda solo la copia, non l'originale. Il comando ref invia i dati effettivi ed eventuali cambiamenti influenzerà i dati al di fuori della funzione.

Ok alla parte confusa, tipi di riferimento:

Consente di creare un tipo di riferimento:

List<string> someobject = new List<string>()

Quando si nuove fino someObject , vengono create due parti:

  1. Il blocco di memoria che contiene i dati per someObject .
  2. Un riferimento (puntatore) per quel blocco dei dati.

Ora, quando si invia in someObject in un metodo senza ref copia il di riferimento puntatore, non i dati. Così ora avete questo:

(outside method) reference1 => someobject
(inside method)  reference2 => someobject

Due riferimenti puntano allo stesso oggetto. Se si modifica una proprietà su someObject usando reference2 interesserà gli stessi dati puntato da riferimento1.

 (inside method)  reference2.Add("SomeString");
 (outside method) reference1[0] == "SomeString"   //this is true

Se NULL fuori reference2 o puntare a nuovi dati non influenzerà punti REFERENCE1 né la REFERENCE1 dati.

(inside method) reference2 = new List<string>();
(outside method) reference1 != null; reference1[0] == "SomeString" //this is true

The references are now pointing like this:
reference2 => new List<string>()
reference1 => someobject

Ora che cosa succede quando si invia someObject da ref a un metodo? Il riferimento effettivo per someObject viene inviato al metodo. Così ora avete solo riferimento ai dati:

(outside method) reference1 => someobject;
(inside method)  reference1 => someobject;

Ma che cosa significa? Agisce esattamente lo stesso come l'invio di someObject non da ref tranne che per due cosa principale:

1) Quando si NULL il riferimento all'interno del metodo sarà nullo quello di fuori del metodo.

 (inside method)  reference1 = null;
 (outside method) reference1 == null;  //true

2) A questo punto è possibile puntare il riferimento ad una posizione di dati completamente diverso e il riferimento al di fuori della funzione sarà ora puntare alla nuova posizione dei dati.

 (inside method)  reference1 = new List<string>();
 (outside method) reference1.Count == 0; //this is true

ref è in e il .

Si dovrebbe usare out di preferenza laddove è sufficiente per le vostre esigenze.

fuori:

In C# un metodo può restituire un solo valore.Se desideri restituire più di un valore, puoi utilizzare la parola chiave out.Il modificatore out restituisce come ritorno per riferimento.La risposta più semplice è che la parola chiave “out” viene utilizzata per ottenere il valore dal metodo.

  1. Non è necessario inizializzare il valore nella funzione chiamante.
  2. È necessario assegnare il valore nella funzione chiamata, altrimenti il ​​compilatore segnalerà un errore.

rif:

In C#, quando passi un tipo di valore come int, float, double ecc.come argomento del parametro del metodo, viene passato per valore.Pertanto, se si modifica il valore del parametro, ciò non influisce sull'argomento nella chiamata al metodo.Ma se contrassegni il parametro con la parola chiave "ref", si rifletterà nella variabile effettiva.

  1. È necessario inizializzare la variabile prima di chiamare la funzione.
  2. Non è obbligatorio assegnare alcun valore al parametro ref nel metodo.Se non si modifica il valore, che bisogno c'è di contrassegnarlo come "ref"?

Estensione dell'esempio Cane, Gatto. Il secondo metodo con rif modifica l'oggetto riferito dal chiamante. Quindi "Gatto" !!!

    public static void Foo()
    {
        MyClass myObject = new MyClass();
        myObject.Name = "Dog";
        Bar(myObject);
        Console.WriteLine(myObject.Name); // Writes "Dog". 
        Bar(ref myObject);
        Console.WriteLine(myObject.Name); // Writes "Cat". 
    }

    public static void Bar(MyClass someObject)
    {
        MyClass myTempObject = new MyClass();
        myTempObject.Name = "Cat";
        someObject = myTempObject;
    }

    public static void Bar(ref MyClass someObject)
    {
        MyClass myTempObject = new MyClass();
        myTempObject.Name = "Cat";
        someObject = myTempObject;
    }

Dato che si sta passando un tipo di riferimento (una classe) non c'è ref uso bisogno perché è passata per impostazione predefinita solo un di riferimento per l'oggetto reale e quindi si cambia sempre l'oggetto dietro la di riferimento.

Esempio:

public void Foo()
{
    MyClass myObject = new MyClass();
    myObject.Name = "Dog";
    Bar(myObject);
    Console.WriteLine(myObject.Name); // Writes "Cat".
}

public void Bar(MyClass someObject)
{
    someObject.Name = "Cat";
}

Per quanto tempo si passa in una classe che non è necessario utilizzare ref se si desidera modificare l'oggetto all'interno del vostro metodo.

ref e out si comportano in modo simile ad eccezione seguenti differenze.

  • variabile ref deve essere inizializzato prima dell'uso. variabile out può essere utilizzato senza assegnazione
  • parametro out deve essere considerato come un valore assegnato dalla funzione che lo utilizza. Quindi, siamo in grado di utilizzare il parametro out inizializzata nel codice chiamante, ma il valore verrà perso quando la funzione esegue.

Per coloro che imparano con l'esempio (come me) ecco cosa Anthony Kolesov sta dicendo .

Ho creato alcuni esempi minime di ref, out, e altri per illustrare il punto. Non sto coprendo le migliori pratiche, solo esempi per capire le differenze.

https://gist.github.com/2upmedia/6d98a57b68d849ee7091

"Baker"

Questo perché il primo cambia la stringa di riferimento per puntare a "Baker". Cambiare il riferimento è possibile perché è stato superato tramite la parola chiave ref (=> un riferimento a un riferimento a una stringa). La seconda chiamata riceve una copia del riferimento alla stringa.

stringa sembra una sorta di speciale in un primo momento. Ma stringa è solo una classe di riferimento e se si definisce

string s = "Able";

poi s è un riferimento a una classe di stringa che contiene il testo "Able"! Un'altra assegnazione alla stessa variabile tramite

s = "Baker";

non cambia la stringa originale, ma solo crea una nuova istanza e lasciare il punto s a tale istanza!

Si può provare con il seguente esempio di codice poco:

string s = "Able";
string s2 = s;
s = "Baker";
Console.WriteLine(s2);

Che cosa si aspetta? Che cosa otterrete è ancora "in grado", perché è sufficiente impostare il riferimento in s ad un'altra istanza mentre i punti S2 per l'istanza originale.

EDIT: stringa è anche immutabile, che significa semplicemente non c'è metodo o proprietà che modifica un'istanza stringa esistente (si può provare a trovare uno nella documentazione, ma non sarà possibile alcun pinne :-)). Tutti i metodi di manipolazione stringa restituita una nuova istanza di stringa! (Ecco perché spesso si ottiene una migliore performance quando si utilizza la classe StringBuilder)

ref significa che il valore del parametro ref è già impostato, il metodo può leggere e modificarlo. Utilizzando la parola chiave ref è equivale a dire che il chiamante è responsabile dell'inizializzazione del valore del parametro.


dice al compilatore che l'inizializzazione dell'oggetto è la responsabilità di la funzione, la funzione deve assegnare al parametro out. Non è consentito lasciare non assegnato.

Out: Una dichiarazione di ritorno può essere utilizzato per restituire un solo valore da una funzione. Tuttavia, utilizzando i parametri di output, è possibile restituire due valori da una funzione. Parametri di uscita sono come parametri di riferimento, eccetto che trasferiscono dati dal metodo piuttosto che in esso.

Il seguente esempio illustra questo:

using System;

namespace CalculatorApplication
{
   class NumberManipulator
   {
      public void getValue(out int x )
      {
         int temp = 5;
         x = temp;
      }

      static void Main(string[] args)
      {
         NumberManipulator n = new NumberManipulator();
         /* local variable definition */
         int a = 100;

         Console.WriteLine("Before method call, value of a : {0}", a);

         /* calling a function to get the value */
         n.getValue(out a);

         Console.WriteLine("After method call, value of a : {0}", a);
         Console.ReadLine();

      }
   }
}

ref: Un parametro di riferimento è un riferimento ad una locazione di memoria di una variabile. Quando si passa i parametri per riferimento, a differenza dei parametri di valore, una nuova posizione di archiviazione non è stato creato per questi parametri. I parametri di riferimento rappresentano la stessa posizione di memoria i parametri effettivi che vengono forniti al metodo.

In C #, si dichiarano i parametri di riferimento utilizzando la parola chiave ref. Nell'esempio seguito viene illustrato questo:

using System;
namespace CalculatorApplication
{
   class NumberManipulator
   {
      public void swap(ref int x, ref int y)
      {
         int temp;

         temp = x; /* save the value of x */
         x = y;   /* put y into x */
         y = temp; /* put temp into y */
       }

      static void Main(string[] args)
      {
         NumberManipulator n = new NumberManipulator();
         /* local variable definition */
         int a = 100;
         int b = 200;

         Console.WriteLine("Before swap, value of a : {0}", a);
         Console.WriteLine("Before swap, value of b : {0}", b);

         /* calling a function to swap the values */
         n.swap(ref a, ref b);

         Console.WriteLine("After swap, value of a : {0}", a);
         Console.WriteLine("After swap, value of b : {0}", b);

         Console.ReadLine();

      }
   }
}

ref e fuori il lavoro, proprio come passando da riferimenti e passando per i puntatori come in C ++.

Per ref, l'argomento deve dichiarato e inizializzato.

Per fuori, l'argomento deve dichiarata ma può o non può essere inizializzato

        double nbr = 6; // if not initialized we get error
        double dd = doit.square(ref nbr);

        double Half_nbr ; // fine as passed by out, but inside the calling  method you initialize it
        doit.math_routines(nbr, out Half_nbr);

Tempo di creazione:

(1) Creiamo il metodo di chiamata Main()

(2) crea un oggetto List (che è un oggetto di tipo riferimento) e lo memorizza nella variabile myList.

public sealed class Program 
{
    public static Main() 
    {
        List<int> myList = new List<int>();

Durante l'esecuzione:

(3) Il runtime alloca una memoria sullo stack al numero 00, sufficientemente ampia da memorizzare un indirizzo (#00 = myList, poiché i nomi delle variabili sono in realtà solo alias per posizioni di memoria)

(4) Il runtime crea un oggetto elenco sull'heap nella posizione di memoria #FF (tutti questi indirizzi sono ad esempio sake)

(5) Il runtime memorizzerà quindi l'indirizzo iniziale #FF dell'oggetto in #00 (o in parole, memorizzerà il riferimento dell'oggetto List nel puntatore myList)

Torna al momento della creazione:

(6) Passiamo quindi l'oggetto List come argomento myParamList al metodo chiamato modifyMyList e assegnargli un nuovo oggetto Elenco

List<int> myList = new List<int>();

List<int> newList = ModifyMyList(myList)

public List<int> ModifyMyList(List<int> myParamList){
     myParamList = new List<int>();
     return myParamList;
}

Durante l'esecuzione:

(7) Il runtime avvia la routine di chiamata per il metodo chiamato e come parte di essa controlla il tipo di parametri.

(8) Dopo aver trovato il tipo di riferimento, alloca una memoria nello stack al numero 04 per l'alias della variabile parametro myParamList.

(9) Quindi memorizza anche il valore #FF.

(10) Il runtime crea un oggetto elenco sull'heap nella posizione di memoria #004 e sostituisce #FF in #04 con questo valore (o ha dereferenziato l'oggetto Elenco originale e ha puntato al nuovo oggetto Elenco in questo metodo)

L'indirizzo in #00 non viene alterato e mantiene il riferimento a #FF(o all'originale myList il puntatore non viene disturbato).


IL rif parola chiave è una direttiva del compilatore per saltare la generazione del codice runtime per (8) e (9), il che significa che non ci sarà alcuna allocazione heap per i parametri del metodo.Utilizzerà il puntatore originale #00 per operare sull'oggetto su #FF.Se il puntatore originale non è inizializzato, il runtime si fermerà lamentandosi di non poter procedere poiché la variabile non è inizializzata

IL fuori parola chiave è una direttiva del compilatore che è più o meno la stessa di ref con una leggera modifica in (9) e (10).Il compilatore si aspetta che l'argomento non sia inizializzato e continuerà con (8), (4) e (5) per creare un oggetto sull'heap e memorizzare il suo indirizzo iniziale nella variabile dell'argomento.Non verrà generato alcun errore non inizializzato e qualsiasi riferimento precedente memorizzato andrà perso.

Sono praticamente la stessa - l'unica differenza è che una variabile si passa come un parametro out non ha bisogno di essere inizializzato, e il metodo che utilizza il parametro ref deve impostare a qualcosa

.
int x;    Foo(out x); // OK 
int y;    Foo(ref y); // Error

parametri Ref sono per i dati che possono essere modificati, i punti principali sono per i dati che è un'uscita aggiuntiva per la funzione (ad esempio int.TryParse) che stanno già utilizzando il valore di cambio di qualcosa.

Di seguito ho mostrato un esempio utilizzando entrambi Rif E fuori.Adesso avrete tutto il nulla osta riguardo all'arbitro e all'uscita.

Nell'esempio riportato di seguito quando commento //myRefObj = new myClass { Name = "rif esterno chiamato!!"};linea, verrà visualizzato un errore che dice "Utilizzo della variabile locale non assegnata 'myRefObj'", ma non è presente alcun errore del genere fuori.

Dove utilizzare Rif:quando chiamiamo una procedura con un parametro in e lo stesso parametro verrà utilizzato per memorizzare l'output di quel proc.

Dove utilizzare Out: quando chiamiamo una procedura senza parametro e lo stesso parametro verrà utilizzato per restituire il valore da quel proc.Da notare anche l'output

public partial class refAndOutUse : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
        myClass myRefObj;
        myRefObj = new myClass { Name = "ref outside called!!  <br/>" };
        myRefFunction(ref myRefObj);
        Response.Write(myRefObj.Name); //ref inside function

        myClass myOutObj;
        myOutFunction(out myOutObj);
        Response.Write(myOutObj.Name); //out inside function
    }

    void myRefFunction(ref myClass refObj)
    {
        refObj.Name = "ref inside function <br/>";
        Response.Write(refObj.Name); //ref inside function
    }
    void myOutFunction(out myClass outObj)
    {
        outObj = new myClass { Name = "out inside function <br/>" }; 
        Response.Write(outObj.Name); //out inside function
    }
}

public class myClass
{
    public string Name { get; set; }
} 
 public static void Main(string[] args)
    {
        //int a=10;
        //change(ref a);
        //Console.WriteLine(a);
        // Console.Read();

        int b;
        change2(out b);
        Console.WriteLine(b);
        Console.Read();
    }
    // static void change(ref int a)
    //{
    //    a = 20;
    //}

     static void change2(out int b)
     {
         b = 20;
     }

è possibile controllare il codice che si descriverà la sua differnce completa quando si utilizza "ref" la sua media che u già inizializzare che int / string

ma  quando si utilizza "out" funziona in entrambe le condizioni wheather u inizializzare che int / string o no ma u deve inizializzare che int / string in quella funzione

Ref: La parola chiave ref viene utilizzato per passare un argomento come riferimento. Ciò significa che quando il valore di tale parametro viene modificato nel metodo, si riflette nel metodo chiamante. Un argomento che viene passato utilizzando una parola chiave ref deve essere inizializzato nel metodo chiamante prima di essere passato al metodo chiamato.

Out: La parola chiave out è utilizzato anche per passare un argomento come parola chiave ref, ma l'argomento può essere passato senza assegnare alcun valore. Un argomento che viene passato utilizzando una parola chiave out deve essere inizializzato nel metodo chiamato prima di ritornare di nuovo a chiamare il metodo.

public class Example
{
 public static void Main() 
 {
 int val1 = 0; //must be initialized 
 int val2; //optional

 Example1(ref val1);
 Console.WriteLine(val1); 

 Example2(out val2);
 Console.WriteLine(val2); 
 }

 static void Example1(ref int value) 
 {
 value = 1;
 }
 static void Example2(out int value) 
 {
 value = 2; 
 }
}

/* Output     1     2     

Rif e nel metodo di sovraccarico

Sia ref e partenza non possono essere utilizzati in modo sovraccarico simultaneamente. Tuttavia, ref e fuori sono trattati in modo diverso a run-time, ma sono trattati nello stesso al momento della compilazione (CLR non distingue tra i due, mentre ha creato IL per ref e fuori).

Dal punto di vista di un metodo che riceve un parametro, la differenza tra ref e out è che C # richiede che i metodi devono scrivere a tutti i parametri out prima di tornare, e non deve fare nulla con un tale parametro, diverso da quello che passa come un parametro out o scrivendo ad esso, finché non è stato né passato come parametro out ad un altro metodo o scritta direttamente. Si noti che alcune altre lingue non impongono tali requisiti; un metodo virtuale o interfaccia che è dichiarato in C # con un parametro out può essere ignorata in un'altra lingua che non impone alcuna restrizione speciali su tali parametri.

Dal punto di vista del chiamante, C # volontà in molte circostanze assumono quando si chiama un metodo con un parametro out farà sì che la variabile passata da scrivere senza essere stato letto prima. Questa ipotesi potrebbe non essere corretto quando si chiama metodi scritti in altre lingue. Ad esempio:

struct MyStruct
{
   ...
   myStruct(IDictionary<int, MyStruct> d)
   {
     d.TryGetValue(23, out this);
   }
}

Se myDictionary identifica un'implementazione IDictionary<TKey,TValue> scritto in una lingua diversa da C #, anche se MyStruct s = new MyStruct(myDictionary); si presenta come un incarico, potrebbe potenzialmente lasciare s non modificato.

Si noti che i costruttori scritti in VB.NET, a differenza di quelli in C #, non fanno ipotesi sul fatto che detti metodi a modificare alcun parametro out, e cancellare tutti i campi incondizionatamente. Il comportamento strano accennato sopra non si verificherà con il codice scritto interamente in VB o interamente in C #, ma può verificarsi quando il codice scritto in C # chiama un metodo scritta in VB.NET.

Se si vuole passare il parametro come un ref allora si dovrebbe inizializzare prima di passare parametro alla funzione altro compilatore stesso mostrerà l'error.But in caso di parametro out non è necessario inizializzare il parametro oggetto prima di passare al method.You può inizializzare l'oggetto nel metodo chiamante stesso.

Oltre a consentire di riassegnare variabile di qualcun altro a una diversa istanza di una classe, ritorni più valori, ecc, utilizzando ref o out lascia qualcun altro sa che cosa avete bisogno da loro e che cosa si intende fare con la variabile che forniscono

  • non è necessario ref o out se tutto quello che stai andando a fare è modificare le cose all'interno l'istanza MyClass che viene passato nella someClass argomento .

    • Il metodo chiamando vedrà cambiamenti come someClass.Message = "Hello World" se usate ref, out o niente
    • Scrivendo someClass = new MyClass() all'interno swap myFunction(someClass) l'oggetto visto dal someClass nell'ambito del solo metodo myFunction. Il metodo di chiamata sa ancora circa l'istanza MyClass originale che ha creato e passato al metodo
  • necessità ref o out se hai intenzione di scambiare il someClass fuori per un intero oggetto nuovo e desidera che il metodo chiamante per vedere le modifiche

    • Scrivendo someClass = new MyClass() all'interno myFunction(out someClass) cambia l'oggetto visto con il metodo che ha chiamato myFunction
Esistono

Altri programmatori

E loro vogliono sapere che cosa si sta andando a che fare con i loro dati. Immaginate che si sta scrivendo una libreria che verrà utilizzato da milioni di sviluppatori. Si desidera loro di sapere quello che stai andando a che fare con loro variabili quando chiamano i metodi

  • Utilizzando ref fa una dichiarazione di "passare una variabile assegnata a un certo valore quando si chiama il mio metodo. Essere consapevoli del fatto che io possa cambiare per qualcosa di completamente diverso nel corso del mio metodo. Non aspettatevi la variabile essere indicando il vecchio oggetto quando ho finito "

  • Utilizzando out fa una dichiarazione di "passare una variabile segnaposto per il mio metodo Non importa se ha un valore o no,. Il compilatore mi costringerà a assegnarlo a un nuovo valore ho assolutamente garanzia. che l'oggetto puntato da vostra variabile prima hai chiamato il mio metodo, essere diverso per il momento ho finito

A proposito, in C # 7.2 c'è un modificatore in anche

E che impedisce il metodo da sostituendo il passato in grado per un esempio differente. Pensate a come dire a quei milioni di sviluppatori "passami il vostro riferimento variabile originale, e prometto di non scambiare i dati con cura artigianale per qualcos'altro". in ha alcune peculiarità, ed in alcuni casi, come in cui potrebbe essere necessaria una conversione implicita di rendere il vostro breve compatibile con un in int il compilatore temporaneamente fare un int, allargare la breve ad esso, passarlo per riferimento e finire. Si può fare questo perché hai dichiarato che non stai andando a pasticciare con essa.


Microsoft ha fatto questo con i metodi .TryParse sui tipi numerici:

int i = 98234957;
bool success = int.TryParse("123", out i);

Con segnalazione il parametro come out stanno dichiarando attivamente qui "siamo sicuramente di andare a modificare il valore faticosamente artigianale di 98.234.957 per qualcos'altro"

Naturalmente, un pò devono, per cose come l'analisi di tipi di valore, perché se il metodo parse non è stato permesso di scambiare il tipo di valore per qualcos'altro che non avrebbe funzionato molto bene .. Ma immaginate c'era un po 'fittizia metodo in qualche libreria che si sta creando:

public void PoorlyNamedMethod(out SomeClass x)

Si può vedere si tratta di un out, e si può quindi sapere che se si spendono ore macinare numeri, creando la perfetta SomeClass:

SomeClass x = SpendHoursMakingMeAPerfectSomeClass();
//now give it to the library
PoorlyNamedMethod(out x);

Bene che era una perdita di tempo, prendendo tutte quelle ore per fare quella classe perfetto. E 'sicuramente sta per essere gettato via e Replaced da PoorlyNamedMethod

Mente bene che il parametro di riferimento che viene fatto passare all'interno della funzione è direttamente lavorato.

Ad esempio,

    public class MyClass
    {
        public string Name { get; set; }
    }

    public void Foo()
    {
        MyClass myObject = new MyClass();
        myObject.Name = "Dog";
        Bar(myObject);
        Console.WriteLine(myObject.Name); // Writes "Dog".
    }

    public void Bar(MyClass someObject)
    {
        MyClass myTempObject = new MyClass();
        myTempObject.Name = "Cat";
        someObject = myTempObject;
    }

Questa scriverà cane, non Cat. Quindi si dovrebbe lavorare direttamente su someObject.

Non può essere così bravo in questo, ma sicuramente stringhe (anche se sono tecnicamente riferimento tipi e vivere nel mucchio) sono passati per valore, non riferimento?

        string a = "Hello";

        string b = "goodbye";

        b = a; //attempt to make b point to a, won't work.

        a = "testing";

        Console.WriteLine(b); //this will produce "hello", NOT "testing"!!!!

Questo perché è necessario ref se si desidera che le modifiche di esistere al di fuori del campo di applicazione della funzione di fare loro, non stanno passando un riferimento in caso contrario.

Per quanto ne so è necessario solo per rif struct / tipi di valore e stringa stessa, come stringa è un tipo di riferimento che finge è, ma non è un tipo di valore.

Potrei essere completamente sbagliato qui, però, sono nuovo.

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