Perché non posso convertire da "out BaseClass" a "out DerivedClass"?
Ho appena imparato che avere un argomento generico come tipo di un outparametro costringe quel tipo generico ad essere invariante. Questo è sorprendente per me. Ho pensato che i outparametri siano trattati allo stesso modo dei tipi restituiti (cioè se il parametro generico è covariante, allora può essere usato in come outparametro out), poiché sono entrambi "output" di un metodo.
Dopo un po 'di indagini, ho capito che non puoi farlo:
public class Program {
public static void Main() {
// cannot convert from 'out object' to 'out string'
F(out object s); // passing an out object
}
public static void F(out string o) {
o = null;
}
}
Questo spiega perché i outparametri devono essere invarianti. Tuttavia, continuo a non capire perché non puoi farlo. Come è noto, i outparametri sono solo un altro modo per restituire un valore. Fpotrebbe essere riscritto con un valore di ritorno e funzionerà:
// This is the semantically equivalent version of the above, just without "out"
public class Program {
public static void Main() {
object s = F();
}
public static string F() {
return null;
}
}
Allora perché il primo frammento di codice non viene compilato? L'uso outconsente Fdi fare qualcosa che non può essere fatto con i valori di ritorno, che interromperà l'indipendenza dai tipi se gli out object sfosse passato un?
Ho trovato questa domanda , che riguarda la conversione nell'altro modo, da una classe derivata a una classe base, che chiaramente non è possibile. Non puoi assegnare il valore di ritorno di un metodo che restituisce a objecta una variabile di tipo string, vero?
Quello che sto chiedendo è, dato che puoi assegnare il valore di ritorno di un metodo che ritorna stringa una variabile di tipo object, perché non puoi fare lo stesso con i outparametri? Cioè, perché non puoi passare un out objecta un out stringparametro?
Ho anche letto i documenti e le specifiche , ma non hanno mai menzionato nulla sul fatto che devi passare lo stesso identico tipo in un outparametro, per non parlare di spiegare perché devi farlo.
Risposte
Con i outparametri l'argomento viene passato per riferimento proprio come ref, la differenza è che il valore deve essere assegnato entro la fine del metodo e il riferimento non deve essere inizializzato prima della chiamata. Ma può essere inizializzato prima e il metodo può leggere il valore iniziale .
Dai documenti: https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/out-parameter-modifier
La parola chiave out fa sì che gli argomenti vengano passati per riferimento
È come la parola chiave ref, tranne per il fatto che ref richiede che la variabile venga inizializzata prima di essere passata
Poiché il metodo può leggere la variabile, il riferimento deve essere di tipo stringper funzionare. La lettura blocca la covarianza e l'uscita blocca la controvarianza, quindi l'argomento deve essere invariante.
Come è noto, i parametri out sono solo un altro modo per restituire un valore
Non vero: https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/out
Come modificatore di parametro, che consente di passare un argomento a un metodo per riferimento anziché per valore.
Ciò significa che stai passando un riferimento a un oggetto specifico.
Penso che la tua risposta sia qui: https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/ref
Il passaggio di un tipo di riferimento per riferimento consente al metodo chiamato di sostituire l'oggetto a cui fa riferimento il parametro di riferimento nel chiamante.
Quindi, quando si passa un oggetto alla funzione, si esegue effettivamente un'assegnazione di tipo
derivato <- base
e quando si esegue l'assegnazione dall'interno della funzione
base <- derivato
Pensa che puoi fare qualcosa di simile in c #:
public static void Out(out string s)
{
Thread.Sleep(50);
s = "World";
}
public static void Ref(ref string s)
{
Console.WriteLine(s); // Hello
Thread.Sleep(100);
Console.WriteLine(s); // World
}
string str = "Hello";
new Thread(() => Out(out str)).Start();
new Thread(() => Ref(ref str)).Start();
Se è legale passare string stra object str, ora strpuò essere di qualsiasi tipo, come mantenere il riferimento tra il metodo Out e Ref?