2010-12-23 5 views
42

Ich erhalte den obigen Fehler und kann ihn nicht beheben. Ich googelte ein bisschen, aber ich werde es nicht los.Eine Eigenschaft oder ein Indexer darf nicht als out- oder ref-Parameter übergeben werden

Szenario:

Ich habe Klasse BudgetAllocate, deren Eigenschaft Budget, das von Doppeltyp ist.

In meinem dataAccessLayer,

In einer meiner Klassen Ich versuche, dies zu tun:

double.TryParse(objReader[i].ToString(), out bd.Budget); 

Welche diesen Fehler wirft:

Property or indexer may not be passed as an out or ref parameter at compile time.

ich dies auch versucht:

double.TryParse(objReader[i].ToString().Equals(DBNull.Value) ? "" : objReader[i].ToString(), out bd.Budget); 

Ev Alles andere funktioniert gut und Referenzen zwischen den Schichten sind vorhanden.

+0

In bd.Budget bd ist ein Objekt der Klasse BudgetAllocate. Entschuldigung ich vergaß. – Pratik

+0

möglich Duplikat von [C# -Eigenschaft und Ref-Parameter, warum kein Zucker?] (Http://StackOverflow.com/Questions/529782/c-sharp-Eigenschaft-und-Ref-Parameter-Whie-No-Sugar) –

+0

mögliche Duplikate von [Zugreifen auf Eigenschaften über generischen Typparameter] (http://stackoverflow.com/questions/3059420/accessing-properties-through-generic-type-parameter) –

Antwort

16

können Sie nicht

double.TryParse(objReader[i].ToString(), out bd.Budget); 

ersetzen bd.Budget mit einigen Variable.

double k; 
double.TryParse(objReader[i].ToString(), out k); 
+5

Warum eine zusätzliche Variable verwenden ?? – Pratik

+0

@ Praktik: der out-Modifier sollte zuvor nicht initialisiert werden. – dhinesh

+4

@pratik Sie können eine Eigenschaft nicht als out-Parameter übergeben, da es keine Garantie dafür gibt, dass die Eigenschaft tatsächlich einen Setter hat. Daher benötigen Sie die zusätzliche Variable. – matt

1

Also Budget ist eine Eigenschaft, richtig?

Setzen Sie es lieber zuerst auf eine lokale Variable und legen Sie den Eigenschaftswert darauf fest.

double t = 0; 
double.TryParse(objReader[i].ToString(), out t); 
bd.Budget = t; 
+0

Danke. Aber kann ich wissen warum? – Pratik

7

Setzen Sie den out-Parameter in eine lokale Variable und dann die Variable in bd.Budget:

double tempVar = 0.0; 

if (double.TryParse(objReader[i].ToString(), out tempVar)) 
{ 
    bd.Budget = tempVar; 
} 

aktualisieren: Gerade von MSDN:

Properties are not variables and therefore cannot be passed as out parameters.

http://msdn.microsoft.com/en-us/library/t3c3bfhx(v=vs.80).aspx

83

Andere haben dir gegeben Die Lösung, aber warum dies notwendig ist: eine Eigenschaft ist nur syntaktischer Zucker für eine Methode.

Zum Beispiel, wenn Sie eine Eigenschaft deklarieren Name mit einem Getter und Setter genannt, unter der Haube der Compiler tatsächlich Methoden get_Name() und set_Name(value) genannt erzeugt. Wenn Sie dann aus dieser Eigenschaft lesen und in diese schreiben, übersetzt der Compiler diese Operationen in Aufrufe dieser generierten Methoden.

Wenn Sie dies berücksichtigen, wird es offensichtlich, warum Sie nicht eine Eigenschaft als Ausgabeparameter übergeben kann - Sie würden tatsächlich einen Verweis auf eine Methode, anstatt einen Verweis auf ein Objekt eine variabel sein vorbei, was ein Ausgabeparameter erwartet.

Ein ähnlicher Fall existiert für Indexer.

+13

Ihre Argumentation ist richtig bis zum letzten Bit. Der Parameter out erwartet einen Verweis auf eine * Variable *, nicht auf ein * Objekt *. –

43

Dies ist ein Fall einer undichten Abstraktion. Eine Eigenschaft ist eigentlich eine Methode, die und Zugriffsmethoden für einen Indexer bekommen kompiliert zu get_Index() und set_Index Methoden. Der Compiler macht einen tollen Job, der diese Tatsache versteckt, er übersetzt automatisch eine Zuweisung zu einer Eigenschaft zum Beispiel zu der entsprechenden Methode set_Xxx().

Aber das geht Bauch, wenn Sie einen Methodenparameter als Referenz übergeben. Dazu muss der JIT-Compiler einen Zeiger auf den Speicherort des übergebenen Arguments übergeben. Das Problem ist, dass es keinen gibt. Das Zuweisen des Werts einer Eigenschaft erfordert den Aufruf der Setter-Methode. Die aufgerufene Methode kann den Unterschied zwischen einer übergebenen Variablen und einer übergebenen Eigenschaft nicht erkennen und kann daher nicht wissen, ob ein Methodenaufruf erforderlich ist.

Bemerkenswert ist, dass dies tatsächlich in VB.NET funktioniert. Zum Beispiel:

Class Example 
    Public Property Prop As Integer 

    Public Sub Test(ByRef arg As Integer) 
     arg = 42 
    End Sub 

    Public Sub Run() 
     Test(Prop) '' No problem 
    End Sub 
End Class 

Der VB.NET Compiler dies löst, indem es automatisch den Code für die Run-Methode zu erzeugen, ausgedrückt in C#:

int temp = Prop; 
Test(ref temp); 
Prop = temp; 

Welches ist die Problemumgehung ist, dass Sie auch nutzen können. Nicht ganz sicher, warum das C# -Team nicht den gleichen Ansatz verwendet hat. Möglicherweise, weil sie die möglicherweise teuren Getter- und Setter-Anrufe nicht verbergen wollten. Oder das völlig undiagnostizierbare Verhalten, das Sie erhalten, wenn der Setter Nebenwirkungen hat, die den Eigenschaftswert ändern, werden sie nach der Zuweisung verschwinden. Klassischer Unterschied zwischen C# und VB.NET, C# ist "keine Überraschungen", VB.NET ist "mach es, wenn du kannst".

+13

Sie haben recht, wenn Sie die teuren Anrufe nicht generieren möchten. Ein sekundärer Grund besteht darin, dass die Kopie-in-Kopie-aus-Semantik eine andere Semantik als die Referenz-Semantik hat und es inkonsistent wäre, zwei subtil unterschiedliche Semantiken für die ref-Weitergabe zu haben. (Allerdings gibt es einige seltene Situationen, in denen kompilierte Ausdrucksbäume unglücklicherweise Kopien kopieren.) –

+2

Was wirklich benötigt wird, ist eine größere Vielfalt an Parameter-passing-Modi, so dass der Compiler "copy in/kopiere "wo es angemessen ist, aber quatsch in Fällen, wo es nicht ist. – supercat

5

Möglicherweise Sehenswürdigkeiten - man könnte schreiben Sie Ihre eigene:

//double.TryParse(, out bd.Budget); 
    bool result = TryParse(s, value => bd.Budget = value); 
} 

public bool TryParse(string s, Action<double> setValue) 
{ 
    double value; 
    var result = double.TryParse(s, out value); 
    if (result) setValue(value); 
    return result; 
} 
+0

Schöne Lösung, danke. – redcalx

Verwandte Themen