Recibo el error anterior y no puedo resolverlo. Busqué un poco en Google, pero no puedo deshacerme de él.
Guión:
Tengo clase BudgetAllocate cuya propiedad es presupuesto que es de tipo doble.
En mi dataAccessLayer,
En una de mis clases estoy tratando de hacer esto:
double.TryParse(objReader[i].ToString(), out bd.Budget);
Que está arrojando este error:
La propiedad o el indexador no pueden pasarse como un parámetro de salida o de referencia en el momento de la compilación.
Incluso probé esto:
double.TryParse(objReader[i].ToString().Equals(DBNull.Value) ? "" : objReader[i].ToString(), out bd.Budget);
Todo lo demás funciona bien y las referencias entre capas están presentes.
c#
.net
error-handling
Pratik
fuente
fuente
DataGrid
que rellenara y luego aprendí que solo autos con propiedades. Cambiar a propiedades rompió algunos parámetros de referencia que estaba usando en mis campos. Tengo que definir variables locales para realizar el análisis.Respuestas:
no puede utilizar
double.TryParse(objReader[i].ToString(), out bd.Budget);
reemplace bd.Budget con alguna variable.
double k; double.TryParse(objReader[i].ToString(), out k);
fuente
Otros le han dado la solución, pero en cuanto a por qué es necesario: una propiedad es solo azúcar sintáctica para un método .
Por ejemplo, cuando declaras una propiedad llamada
Name
con un getter y un setter, bajo el capó el compilador genera métodos llamadosget_Name()
yset_Name(value)
. Luego, cuando lee y escribe en esta propiedad, el compilador traduce estas operaciones en llamadas a esos métodos generados.Cuando considera esto, resulta obvio por qué no puede pasar una propiedad como parámetro de salida: en realidad estaría pasando una referencia a un método , en lugar de una referencia a
un objeto ouna variable , que es lo que espera un parámetro de salida.Existe un caso similar para los indexadores.
fuente
Este es un caso de abstracción con fugas. Una propiedad es en realidad un método, los descriptores de acceso get y set para un indexador se compilan en los métodos get_Index () y set_Index. El compilador hace un trabajo estupendo ocultando ese hecho, automáticamente traduce una asignación a una propiedad al método set_Xxx () correspondiente, por ejemplo.
Pero esto se arruina cuando pasa un parámetro de método por referencia. Eso requiere que el compilador JIT pase un puntero a la ubicación de memoria del argumento pasado. El problema es que no hay uno, asignar el valor de una propiedad requiere llamar al método setter. El método llamado no puede diferenciar entre una variable pasada y una propiedad pasada y, por lo tanto, no puede saber si se requiere una llamada al método.
Es de destacar que esto realmente funciona en VB.NET. Por ejemplo:
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
El compilador VB.NET resuelve esto generando automáticamente este código para el método Run, expresado en C #:
int temp = Prop; Test(ref temp); Prop = temp;
Cuál es la solución alternativa que también puede utilizar. No estoy seguro de por qué el equipo de C # no usó el mismo enfoque. Posiblemente porque no querían ocultar las llamadas getter y setter potencialmente costosas. O el comportamiento completamente no diagnosticable que obtendrá cuando el colocador tenga efectos secundarios que cambien el valor de la propiedad, desaparecerán después de la asignación. Diferencia clásica entre C # y VB.NET, C # es "sin sorpresas", VB.NET es "haz que funcione si puedes".
fuente
Coloque el parámetro out en una variable local y luego establezca la variable en
bd.Budget
:double tempVar = 0.0; if (double.TryParse(objReader[i].ToString(), out tempVar)) { bd.Budget = tempVar; }
Actualización : Directamente de MSDN:
fuente
Posiblemente de interés, podría escribir el suyo propio:
//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; }
fuente
Esta es una publicación muy antigua, pero estoy enmendando la aceptada, porque hay una forma aún más conveniente de hacer esto que yo no conocía.
Se llama declaración en línea y es posible que siempre haya estado disponible (como en el uso de declaraciones) o podría haberse agregado con C # 6.0 o C # 7.0 para tales casos, no estoy seguro, pero funciona como un encanto de todos modos:
Inetad de esto
double temp; double.TryParse(objReader[i].ToString(), out temp); bd.Budget = temp;
utilizar esta:
double.TryParse(objReader[i].ToString(), out double temp); bd.Budget = temp;
fuente
Entonces Budget es una propiedad, ¿correcto?
En lugar de eso, primero configúrelo en una variable local y luego establezca el valor de propiedad en eso.
double t = 0; double.TryParse(objReader[i].ToString(), out t); bd.Budget = t;
fuente
Por lo general, cuando intento hacer esto es porque quiero configurar mi propiedad o dejarla en el valor predeterminado. Con la ayuda de esta respuesta y
dynamic
tipos, podemos crear fácilmente un método de extensión de cadena para mantenerlo simple y con líneas.public static dynamic ParseAny(this string text, Type type) { var converter = TypeDescriptor.GetConverter(type); if (converter != null && converter.IsValid(text)) return converter.ConvertFromString(text); else return Activator.CreateInstance(type); }
Úselo así;
bd.Budget = objReader[i].ToString().ParseAny(typeof(double)); // Examples int intTest = "1234".ParseAny(typeof(int)); // Result: 1234 double doubleTest = "12.34".ParseAny(typeof(double)); // Result: 12.34 decimal pass = "12.34".ParseAny(typeof(decimal)); // Result: 12.34 decimal fail = "abc".ParseAny(typeof(decimal)); // Result: 0 string nullStr = null; decimal failedNull = nullStr.ParseAny(typeof(decimal)); // Result: 0
Opcional
En una nota al margen, si es
SQLDataReader
así, también puede hacer uso de lasGetSafeString
extensiones para evitar excepciones nulas por parte del lector.public static string GetSafeString(this SqlDataReader reader, int colIndex) { if (!reader.IsDBNull(colIndex)) return reader.GetString(colIndex); return string.Empty; } public static string GetSafeString(this SqlDataReader reader, string colName) { int colIndex = reader.GetOrdinal(colName); if (!reader.IsDBNull(colIndex)) return reader.GetString(colIndex); return string.Empty; }
Úselo así;
bd.Budget = objReader.GetSafeString(i).ParseAny(typeof(double)); bd.Budget = objReader.GetSafeString("ColumnName").ParseAny(typeof(double));
fuente