Frage

Was bedeutet die Aussage bedeuten?

Von hier

ref und out-Parameter in C # und kann nicht als Variante markiert werden.

1) Bedeutet es, dass die folgenden nicht getan werden kann.

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

2) Oder bedeutet es, ich nicht die folgende haben kann.

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

Ich habe versucht, (2) und es hat funktioniert.

War es hilfreich?

Lösung

"out" ist, grob gesprochen, "erscheint nur in Ausgangspositionen".

"in" ist, grob gesprochen, "erscheint nur in Eingabepositionen".

Die wahre Geschichte ist ein wenig komplizierter als das, aber die Schlüsselwörter wurden ausgewählt, weil die meiste Zeit der Fall ist.

Betrachten wir ein Verfahren zur Herstellung einer Schnittstelle oder das Verfahren durch einen Delegierten vertreten:

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

Does T erscheint in einer Eingabeposition? Ja. Der Anrufer kann einen Wert von T in über Punkt passieren; der Angerufene Foo kann das lesen. Daher T kann nicht markiert "out" sein.

Does T erscheint in einer Ausgangsposition? Ja. Der Angerufene kann einen neuen Wert Artikel schreiben, die der Anrufer dann lesen kann. Daher kann T nicht "in" gekennzeichnet werden.

Deshalb, wenn T in einem "ref" formale Parameter erscheint, kann T nicht als entweder in oder out markiert werden.

Schauen wir uns einige echte Beispiele dafür, wie die Dinge schief gehen. Angenommen, dies war legal:

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

Nun Hund meiner Katzen, sie gerade eine Katze Rinde. "Out" kann nicht legal sein.

Was ist "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);

Und wir haben gerade eine Katze in einer Variablen, die nur Hunde halten können. T kann auch nicht "in" gekennzeichnet werden.

Was ist out-Parameter?

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

? Jetzt T erscheint nur in einer Ausgangsposition. Sollte es legal sein als "out" zu machen T markiert?

Leider nein. „Out“ ist eigentlich nicht anders als „ref“ hinter den Kulissen. Der einzige Unterschied zwischen „out“ und „ref“ ist, dass die Compiler verbietet von einem Out-Parameter zu lesen, bevor sie durch den Angerufenen zugeordnet ist, und dass der Compiler erfordert Zuordnung vor den Angerufenen kehrt normal. Jemand, der eine Implementierung dieser Schnittstelle schrieb in einer .NET-Sprache anders als C # der Lage wäre, aus dem Artikel zu lesen, bevor es initialisiert wurde, und daher könnte es als eine Eingabe verwendet werden. Wir verbieten deshalb T als „out“ in diesem Fall markiert. Das ist bedauerlich, aber nichts, was wir dagegen tun können; wir müssen die Art Sicherheitsregeln der CLR gehorchen.

Darüber hinaus ist die Regel der „out“ Parameter, dass sie nicht für die Eingabe verwendet werden können , bevor sie geschrieben werden . Es gibt keine Regel, dass sie nicht für die Eingabe verwendet werden können nach , die sie geschrieben. Angenommen, wir erlauben

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

Noch einmal haben wir eine Katze Rinde. Wir können nicht zulassen, dass T "out" sein.

Es ist sehr dumm Parameter auf diese Weise für die Eingabe zu verwenden, aber legal.


UPDATE: C # 7 hat in als formale Parameter Erklärung hinzugefügt, was bedeutet, dass wir jetzt beide in und out bedeutet zwei Dinge haben; dies wird einige Verwirrung stiften. Lassen Sie mich die aufklären:

  • in, out und ref auf formale Parameterdeklaration in einer Parameterliste bedeutet "Dieser Parameter ist ein Alias ??für eine Variable vom Anrufer geliefert".
  • ref bedeutet „die Angerufene die aliased Variable lesen oder schreiben kann, und es muss bekannt sein, bevor der Anruf zugewiesen werden.
  • out bedeutet „die Angerufene muss den veränderten Variable über den Alias ??schreiben, bevor es in der Regel gibt“. Es bedeutet auch, dass der Angerufene die aliased Variable über den Alias ??nicht lesen muss, bevor er es schreibt, da die Variable nicht eindeutig zugewiesen werden könnte.
  • in bedeutet „die Angerufene kann die aliased Variable lesen, aber nicht schreiben über die Alias ??es“. Der Zweck in ist ein seltene Leistung Problem zu lösen, wodurch eine große Struktur muss „von Wert“ übergeben werden, aber es ist teuer, dies zu tun. Als Implementierungsdetails werden Parameter in typischerweise über einen Zeiger großen Wert übergeben, die von Wert als das Kopieren schneller ist, aber langsamer auf den dereferenzieren.
  • Aus der Sicht des CLR, in, out und ref sind alle die gleiche Sache; die Regeln über die Lese- und Schreibvorgänge, welche Variablen zu welchen Zeiten, the CLR wissen oder Pflege nicht.
  • Da es die CLR, dass erzwingt Regeln Varianz, die Regeln gelten auch für ref und in Parameter gelten für out.

Im Gegensatz dazu in und out auf Typparameter Erklärungen bedeuten „dieser Typ Parameter nicht in einer kovarianten Weise verwendet werden müssen“ und „diese Art Parameter nicht in einer Art und Weise kontra verwendet werden müssen“, respectively.

Wie oben erwähnt, wir wählten in und out für diese Modifikatoren, denn wenn wir IFoo<in T, out U> sehen dann T in „Eingang“ Positionen verwendet wird und U wird in „Output“ Positionen verwendet. Obwohl das nicht ist streng wahr, es ist wahr genug, um im Anwendungsfall 99,9%, dass es eine hilfreiche mnemonic ist.

Es ist bedauerlich, dass interface IFoo<in T, out U> { void Foo(in T t, out U u); } illegal ist, weil es aussieht wie es funktionieren soll. Es kann nicht funktionieren, weil aus Sicht des CLR Verifizierer, die beide ref Parameter sind und daher read-write.

Dies ist nur einer jener seltsamen, unbeabsichtigten Situationen, in denen zwei Funktionen, die logisch sollen zusammen arbeiten nicht gut zusammen Gründe für die Implementierung Detail arbeiten.

Andere Tipps

Es bedeutet, dass Sie nicht die folgende Erklärung haben:

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

Edit: @Eric Lippert korrigiert mich, dass dies eine noch legal ist:

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

Es macht wirklich Sinn, da die R generischen Parameter nicht als Variante markiert ist, damit es nicht die Regel verstoßen. Doch dies ist immer noch illegal:

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

Es ist möglich, Kovarianz mit out-Parameter zu verwenden, aber Sie müssen zwei Strukturen. Zum Beispiel können Sie die Out-Parameter auf einer Erweiterungsmethode setzen:

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

Aber kann der folgende Code kompiliert werden:

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!
}
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top