Domanda

Che cosa fa la media dichiarazione?

Da qui

  

ref e fuori parametri in C # e   non può essere contrassegnato come variante.

1) Vuol dire che quanto segue non può essere fatto.

public class SomeClass<R, A>: IVariant<R, A>
{
    public virtual R DoSomething( ref A args )
    {
        return null;
    }
}

2) Oppure significa che non posso avere la seguente.

public delegate R Reader<out R, in A>(A arg, string s);

public static void AssignReadFromPeonMethodToDelegate(ref Reader<object, Peon> pReader)
{
    pReader = ReadFromPeon;
}

static object ReadFromPeon(Peon p, string propertyName)
    {
        return p.GetType().GetField(propertyName).GetValue(p);
    }

static Reader<object, Peon> pReader;

static void Main(string[] args)
    {
        AssignReadFromPeonMethodToDelegate(ref pReader);
        bCanReadWrite = (bool)pReader(peon, "CanReadWrite");

        Console.WriteLine("Press any key to quit...");
        Console.ReadKey();
    }

ho provato (2) e ha funzionato.

È stato utile?

Soluzione

"out" significa, grosso modo, "appare solo in posizioni di uscita".

"in" significa, grosso modo, "appare solo nelle posizioni di ingresso".

La vera storia è un po 'più complicato di così, ma le parole chiave sono stati scelti perché la maggior parte delle volte questo è il caso.

Si consideri un metodo di un'interfaccia o il metodo rappresentato da un delegato:

delegate void Foo</*???*/ T>(ref T item);

La T appaiono in una posizione di ingresso? Sì. Il chiamante può passare un valore di T in via voce; il chiamato Foo può leggere questo. Quindi T non può essere contrassegnato "fuori".

La T appaiono in una posizione di uscita? Sì. Il chiamato può scrivere un nuovo valore alla voce, che il chiamante può quindi leggere. Quindi T non può essere contrassegnato "in".

Quindi se T appare in una "ref" parametro formale, T non può essere contrassegnato come sia dentro o fuori.

Diamo un'occhiata ad alcuni esempi reali di come le cose vanno male. Supponiamo che questo fosse legale:

delegate void X<out T>(ref T item);
...
X<Dog> x1 = (ref Dog d)=>{ d.Bark(); }
X<Animal> x2 = x1; // covariant;
Animal a = new Cat();
x2(ref a);

Bene cane i miei gatti, abbiamo appena fatto una corteccia gatto. "Out" non può essere legale.

Che dire di "in"?

delegate void X<in T>(ref T item);
...
X<Animal> x1 = (ref Animal a)=>{ a = new Cat(); }
X<Dog> x2 = x1; // contravariant;
Dog d = new Dog();
x2(ref d);

E abbiamo appena messo un gatto in una variabile che può contenere solo i cani. T non può essere contrassegnato "in" sia.

Che dire di un parametro out?

delegate void Foo</*???*/T>(out T item);

? Ora T appare solo in una posizione di uscita. Dovrebbe essere legale per rendere T contrassegnato come "out"?

Purtroppo no. "Out" in realtà non è diverso da "ref" dietro le quinte. L'unica differenza tra "fuori" e "ref" è che il compilatore proibisce la lettura da un parametro out prima che venga assegnato dal chiamato, e che il compilatore richiede l'assegnazione prima che il chiamato rendimenti normalmente. Qualcuno che ha scritto un'implementazione di questa interfaccia in un linguaggio .NET diversa da C # sarebbe in grado di leggere dalla voce prima che fosse inizializzato, e quindi potrebbe essere utilizzato come input. Abbiamo quindi proibiamo marcatura T come "out" in questo caso. Questo è deplorevole, ma nulla che possiamo fare a questo proposito; dobbiamo obbedire alle regole di sicurezza di tipo del CLR.

Inoltre, la regola del "fuori" i parametri è che essi non possono essere utilizzati per l'ingresso prima di essere scritti . Non c'è una regola che non possono essere utilizzate per l'ingresso dopo sono scritti a. Supponiamo di permesso

delegate void X<out T>(out T item);
class C
{
    Animal a;
    void M()
    {
        X<Dog> x1 = (out Dog d) => 
        { 
             d = null; 
             N(); 
             if (d != null) 
               d.Bark(); 
        };
        x<Animal> x2 = x1; // Suppose this were legal covariance.
        x2(out this.a);
    }
    void N() 
    { 
        if (this.a == null) 
            this.a = new Cat(); 
    }
}

Ancora una volta abbiamo fatto una corteccia gatto. Non possiamo permettere T di essere "fuori".

E 'molto sciocco di utilizzare i punti principali per l'ingresso in questo modo, ma legale.


UPDATE: C # 7 ha aggiunto in come una dichiarazione parametro formale, il che significa che ora abbiamo entrambi in e out che significa due cose; questo sta andando a creare una certa confusione. Vorrei chiarire che fino:

  • in, out e ref su un dichiarazione di parametro formale in una lista di parametri significa "Questo parametro è un alias a una variabile fornito dal chiamante".
  • mezzi ref "il chiamato può leggere o scrivere la variabile alias, e deve essere conosciuto da assegnare prima della chiamata.
  • mezzi out "il chiamato devono scrivere la variabile alias tramite l'alias prima di ritornare normalmente". E significa anche che il chiamato non deve leggere la variabile alias tramite l'alias prima che lo scrive, perché la variabile potrebbe non essere definitivamente assegnato.
  • mezzi in "il chiamato può leggere la variabile alias, ma non scrive ad esso tramite l'alias". Lo scopo di in è quello di risolvere un problema di prestazioni rara, per cui una grande struct deve essere passato "per valore", ma è costoso da fare così. Come un dettaglio di implementazione, i parametri in sono tipicamente passati attraverso un valore di puntatore di dimensioni, che è più veloce rispetto alla copia per valore, ma più lento sulla dereferenzia.
  • Dal punto di vista del CLR, in, out e ref sono tutti la stessa cosa; le regole su chi legge e scrive quali variabili in quale momento, the CLR non conosce o di cura.
  • Dal momento che è il CLR che applica le regole circa varianza, le regole che si applicano a ref si applicano anche ai parametri in e out.

Al contrario, in e out su dichiarazioni di parametri di tipo significa "questo parametro tipo non deve essere utilizzato in modo covariante" e "questo parametro tipo non deve essere utilizzato in maniera controvariante", rispettivamente.

Come indicato in precedenza, abbiamo scelto in e out per tali modificatori perché se vediamo IFoo<in T, out U> allora T viene utilizzato in posizioni "ingresso" e U viene utilizzato in posizioni "uscita". Anche se questo non è solo vero, è abbastanza vero nel caso di utilizzo del 99,9% che si tratta di un mnemonico utile.

E 'un peccato che interface IFoo<in T, out U> { void Foo(in T t, out U u); } è illegale, perché sembra che dovrebbe funzionare. Non può funzionare perché dal punto di vista del verificatore CLR, questi sono entrambi i parametri ref e quindi lettura e scrittura.

Questa è solo una di quelle strane, situazioni indesiderate in cui due caratteristiche che logicamente dovrebbero lavorare insieme non funzionano bene insieme per motivi di implementazione di dettaglio.

Altri suggerimenti

Ciò significa che non potete avere la seguente dichiarazione:

public delegate R MyDelegate<out R, in A>(ref A arg);

Modifica @Eric Lippert mi corresse che questo è ancora legale:

public delegate void MyDelegate<R, in A>(A arg, out R s);

Si rende effettivamente senso, dato che il parametro generico R non è contrassegnato come variante, quindi non viola la regola. Tuttavia, questo è ancora illegale:

public delegate void MyDelegate<out R, in A>(A arg, out R s);

E 'possibile utilizzare covarianza con i parametri fuori, ma è necessario due strutture. Per esempio si può mettere il parametro su un metodo di estensione:

public static class ResultExtension{
    public static bool TryGetValue<T>(this IResult<T> self, out T res) {
        if (self.HasValue) {
            res = self.Value;
            return true;

        }
        res = default;
        return false;
    }
}

public interface IResult<out T>
{
    bool HasValue { get; }
    T Value { get; }
}

Ma, il seguente codice può essere compilato:

interface IFoo<out T>
{
    T Get();

    //bool TryGet(out T value); // doesn't work: Invalid variance: The type parameter 'T' must be invariantly valid on 'IFoo<T>.TryGet(out T)'. 'T' is covariant.

    bool TryGet(Action<T> value); // works!
}
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top