Frage

Nehmen Sie die folgenden:

class A {}

class B : A {}

class C
{
    C()
    {
        var b = new B();
        Foo(b);
        Foo2(ref b); // <= compile-time error: 
                     // "The 'ref' argument doesn't match the parameter type"
    }

    void Foo(A a) {}

    void Foo2(ref A a) {}  
}

Warum hat die oben compile-Zeit-Fehler auftreten?Dies geschieht mit den beiden ref und out Argumente.

War es hilfreich?

Lösung

=============

UPDATE:Ich habe diese Antwort als Grundlage für diesen blog-Eintrag:

Warum ref-und out-Parameter nicht zulassen, dass Typ-Veränderung?

Siehe die blog-Seite für weitere Kommentar zu diesem Thema.Vielen Dank für die tolle Frage.

=============

Angenommen, Sie haben Klassen Animal, Mammal, Reptile, Giraffe, Turtle und Tiger, mit der offensichtlichen Unterklassen-Beziehungen.

Nehmen wir nun an, Sie haben eine Methode void M(ref Mammal m). M kann sowohl Lesen und schreiben m.


Übergeben Sie eine variable des Typs Animal zu M?

Nein.Diese variable könnte enthalten ein Turtle, aber M wird davon ausgegangen, dass es enthält nur Säugetiere.Ein Turtle ist nicht ein Mammal.

Fazit 1: ref Parameter können nicht gemacht werden "größer".(Es gibt mehr Tiere als Säugetiere, so wird die variable wird immer "größer", denn es kann enthalten mehr Sachen.)


Übergeben Sie eine variable des Typs Giraffe zu M?

Nein. M schreiben kann m, und M vielleicht schreiben möchten Tiger in m.Jetzt Sie haben eine Tiger in eine variable, das ist eigentlich der Typ Giraffe.

Fazit 2: ref Parameter können nicht gemacht werden "kleinere".


Betrachten wir nun N(out Mammal n).

Übergeben Sie eine variable des Typs Giraffe zu N?

Nein. N schreiben kann n, und N vielleicht schreiben möchten Tiger.

Fazit 3: out Parameter können nicht gemacht werden "kleinere".


Übergeben Sie eine variable des Typs Animal zu N?

Hmm.

Nun, warum nicht? N nicht gelesen werden n, es kann nur schreiben, richtig?Sie schreiben ein Tiger um eine variable des Typs Animal und Sie sind alle eingestellt, richtig?

Falsch.Die Regel ist das nicht "N kann nur schreiben n".

Die Regeln sind kurz:

1) N hat zu schreiben n bevor N returns normal.(Wenn N wirft, sind alle Wetten sind aus.)

2) N zu schreiben, hat etwas zu n bevor es liest etwas aus n.

Ermöglicht dies die Abfolge der Ereignisse:

  • Deklarieren Sie ein Feld x Typ Animal.
  • Pass x als out parameter N.
  • N schreibt ein Tiger in n,, das ist ein alias für x.
  • In einem anderen thread, jemand schreibt ein Turtle in x.
  • N versuche, den Inhalt zu Lesen der n, und entdeckt ein Turtle in dem, was es denkt, ist eine variable des Typs Mammal.

Klar, wir wollen, dass die illegalen.

Fazit 4: out Parameter können nicht gemacht werden "größer".


Abschließendes Fazit: Weder ref noch out die Parameter können variieren, Ihre Typen.Alles andere ist eine Pause überprüfbare Art Sicherheit.

Wenn Sie diese Fragen im basic-Typ-Theorie, die Sie interessieren, Lesen Sie meine Serie auf, wie Kovarianz und Kontravarianz Arbeit in C# 4.0.

Andere Tipps

Da in beiden Fällen müssen Sie in der Lage sein Wert zuweisen / out-Parameter ref.

Wenn Sie versuchen, b in foo2 Verfahren als Referenz zu übergeben, und in foo2 Sie versuchen, eine = new A bis assing (), wäre dies ungültig.
Aus demselben Grund kann man nicht schreiben:

B b = new A();

Sie sind mit dem klassischen OOP Problem der Kovarianz (und Kontra) kämpfen haben, finden Sie unter wikipedia : viel wie dieser Tatsache kann intuitive Erwartungen trotzen, es mathematisch unmöglich ist, die Substitution von abgeleiteten Klassen anstelle von Basis diejenigen für wandelbar (belegbar) Argumente (und auch Behälter, deren Elemente sind belegbar zu ermöglichen, für nur aus dem gleichen Grund), während immer noch Liskov Prinzip respektieren. Warum ist das so ist in den bestehenden Antworten skizziert, und erforschte tiefer in diesem Wiki-Artikel und Links davon.

OOP-Sprachen, die dies angezeigt, während traditionell statisch verbleibenden typsicher ist „Betrug“ (Einfügen versteckten dynamische Typprüfungen oder erfordern Kompilierung-Prüfung aller Quellen zu überprüfen); die grundsätzliche Wahl: entweder auf dieser Kovarianz aufgeben und Verblüffung der Praktizierenden akzeptieren (wie C # hier der Fall ist), oder in einen dynamischen Typisierung Ansatz bewegen (als allererstes OOP-Sprache, Smalltalk, tat) oder unveränderlich bewegen (Single- Zuordnung) Daten, wie funktionale Sprachen (unter Unveränderlichkeit tun, können Sie Kovarianz unterstützen, und auch andere damit zusammenhängende Rätsel wie die Tatsache vermeiden, dass Sie nicht Platz Unterklasse Rechteck in einer wandelbaren-Datenwelt haben können).

Bedenken Sie:

class C : A {}
class B : A {}

void Foo2(ref A a) { a = new C(); } 

B b = null;
Foo2(ref b);

Es würde verletzen Typsicherheit

Während die anderen Antworten haben kurz und bündig die Gründe für dieses Verhalten erklärt, ich glaube, es wert ist zu erwähnen, dass, wenn Sie wirklich etwas dieser Art tun müssen, um Sie, indem foo2 in eine generische Methode ähnliche Funktionalität erreichen können, wie zum Beispiel:

class A {}

class B : A {}

class C
{
    C()
    {
        var b = new B();
        Foo(b);
        Foo2(ref b); // <= no compile error!
    }

    void Foo(A a) {}

    void Foo2<AType> (ref AType a) where AType: A {}  
}

Da ein Foo2 ref B gibt in einem fehlerhaften Objekt führen würde, weil Foo2 nur weiß, wie A Teil B zu füllen.

Ist das nicht der Compiler sagen Sie es möchten Sie ausdrücklich um das Objekt zu werfen, so dass es sicher sein kann, Sie wissen, was Ihre Absichten sind?

Foo2(ref (A)b)

Das macht Sinn aus sicherheitstechnischer Sicht, aber ich hätte es vorgezogen, wenn der Compiler eine Warnung statt einem Fehler gibt, da es rechtmäßige Nutzung der polymoprhic Objekte als Referenz übergeben. z.

class Derp : interfaceX
{
   int somevalue=0; //specified that this class contains somevalue by interfaceX
   public Derp(int val)
    {
    somevalue = val;
    }

}


void Foo(ref object obj){
    int result = (interfaceX)obj.somevalue;
    //do stuff to result variable... in my case data access
    obj = Activator.CreateInstance(obj.GetType(), result);
}

main()
{
   Derp x = new Derp();
   Foo(ref Derp);
}

Dies wird nicht kompilieren, aber es funktionieren würde?

Wenn Sie praktische Beispiele für die Typen verwenden, werden Sie es sehen:

SqlConnection connection = new SqlConnection();
Foo(ref connection);

Und jetzt haben Sie Ihre Funktion, die die Vorfahren nimmt ( das heißt Object.):

void Foo2(ref Object connection) { }

Was möglicherweise daran falsch sein?

void Foo2(ref Object connection)
{
   connection = new Bitmap();
}

Sie schaffte es gerade noch eine Bitmap zu Ihrem SqlConnection zuzuweisen.

Das ist nicht gut.


Versuchen Sie es erneut mit anderen:

SqlConnection conn = new SqlConnection();
Foo2(ref conn);

void Foo2(ref DbConnection connection)
{
    conn = new OracleConnection();
}

Sie stopfte eine OracleConnection über oben auf Ihrem SqlConnection.

In meinem Fall meine Funktion ein Objekt akzeptiert und ich kann an nichts senden, damit ich einfach tat

object bla = myVar;
Foo(ref bla);

Und das funktioniert

Meine Foo sind in VB.NET und prüft nach Art innerhalb und macht eine Menge Logik

Ich entschuldige mich, wenn meine Antwort Duplikat ist aber andere waren zu lang

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top