web-dev-qa-db-fra.com

Une propriété ou un indexeur ne peut pas être passé en tant que paramètre out ou ref

J'obtiens l'erreur ci-dessus et je ne peux pas la résoudre. J'ai googlé un peu mais je ne peux pas m'en débarrasser.

Scénario:

J'ai la classe BudgetAllocate dont la propriété est budget qui est de type double.

Dans mon dataAccessLayer,

Dans une de mes classes, j'essaie de faire ceci:

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

Qui jette cette erreur:

La propriété ou l'indexeur ne peut pas être passé en tant que paramètre out ou ref au moment de la compilation.

J'ai même essayé ceci:

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

Tout le reste fonctionne bien et les références entre les couches sont présentes.

71
Pratik

tu ne peux pas utiliser

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

remplacez bd.Budget par une variable.

double k;
double.TryParse(objReader[i].ToString(), out k); 
29
dhinesh

D'autres vous ont donné la solution, mais pourquoi cela est nécessaire: une propriété est juste du sucre syntaxique pour une méthode.

Par exemple, lorsque vous déclarez une propriété appelée Name avec un getter et un setter, sous le capot, le compilateur génère en fait des méthodes appelées get_Name() et set_Name(value). Ensuite, lorsque vous lisez et écrivez dans cette propriété, le compilateur traduit ces opérations en appels à ces méthodes générées.

Lorsque vous y réfléchissez, il devient évident pourquoi vous ne pouvez pas passer une propriété en tant que paramètre de sortie - vous passeriez en fait une référence à une méthode, plutôt qu'une référence à un objet a variable, ce à quoi s'attend un paramètre de sortie.

Un cas similaire existe pour les indexeurs.

121
Mike Chamberlain

C'est le cas d'une abstraction qui fuit. Une propriété est en fait une méthode, les accesseurs get et set d'un indexeur sont compilés en méthodes get_Index () et set_Index. Le compilateur fait un travail formidable en masquant ce fait, il traduit automatiquement une affectation en propriété à la méthode set_Xxx () correspondante par exemple.

Mais cela remonte lorsque vous passez un paramètre de méthode par référence. Cela nécessite que le compilateur JIT passe un pointeur vers l'emplacement mémoire de l'argument passé. Le problème est qu'il n'y en a pas, pour affecter la valeur d'une propriété, il faut appeler la méthode setter. La méthode appelée ne peut pas faire la différence entre une variable passée et une propriété passée et ne peut donc pas savoir si un appel de méthode est requis.

Il convient de noter que cela fonctionne réellement dans VB.NET. Par exemple:

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

Le compilateur VB.NET résout ce problème en générant automatiquement ce code pour la méthode Run, exprimé en C #:

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

Quelle est la solution de contournement que vous pouvez également utiliser. Je ne sais pas trop pourquoi l'équipe C # n'a pas utilisé la même approche. Peut-être parce qu'ils ne voulaient pas cacher les appels getter et setter potentiellement coûteux. Ou le comportement complètement non diagnostiqué que vous obtiendrez lorsque le setter a des effets secondaires qui modifient la valeur de la propriété, ils disparaîtront après l'affectation. Différence classique entre C # et VB.NET, C # est "sans surprise", VB.NET est "faites-le fonctionner si vous le pouvez".

53
Hans Passant

Placez le paramètre out dans une variable locale, puis définissez la variable dans bd.Budget:

double tempVar = 0.0;

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

Mise à jour: directement depuis MSDN:

Les propriétés ne sont pas des variables et ne peuvent donc pas être passées en tant que paramètres de sortie.

8
Adam Houldsworth

Peut-être intéressant - vous pouvez écrire le vôtre:

    //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;
}
7
David Hollinshead

Il s'agit d'un très ancien poste, mais je modifie l'acceptation, car il existe une façon encore plus pratique de le faire que je ne connaissais pas.

Cela s'appelle une déclaration en ligne et pourrait avoir toujours été disponible (comme dans l'utilisation d'instructions) ou il pourrait avoir été ajouté avec C # 6.0 ou C # 7.0 pour de tels cas, pas sûr, mais fonctionne quand même comme un charme:

Inetad de cette

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

utilisez ceci:

double.TryParse(objReader[i].ToString(), out double temp);
bd.Budget = temp;
3
DanDan

Le budget est donc une propriété, n'est-ce pas?

Plutôt, définissez-le d'abord sur une variable locale, puis définissez la valeur de la propriété sur celle-ci.

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