Estou recebendo o erro acima e não consigo resolvê-lo. Eu pesquisei um pouco, mas não consigo me livrar dele.
Cenário:
Tenho a classe BudgetAllocate, cuja propriedade é orçamento, que é do tipo duplo.
Em meu dataAccessLayer,
Em uma de minhas aulas, estou tentando fazer isso:
double.TryParse(objReader[i].ToString(), out bd.Budget);
O que está gerando este erro:
Propriedade ou indexador não pode ser passado como um parâmetro out ou ref em tempo de compilação.
Eu até tentei isso:
double.TryParse(objReader[i].ToString().Equals(DBNull.Value) ? "" : objReader[i].ToString(), out bd.Budget);
Todo o resto está funcionando bem e as referências entre as camadas estão presentes.
c#
.net
error-handling
Pratik
fonte
fonte
DataGrid
preenchesse a partir de então venho aprendê-lo apenas autos com propriedades. Mudar para propriedades quebrou alguns parâmetros ref que eu estava usando em meus campos. Tem que definir variáveis locais para fazer a análise.Respostas:
você não pode usar
double.TryParse(objReader[i].ToString(), out bd.Budget);
substitua bd.Budget por alguma variável.
double k; double.TryParse(objReader[i].ToString(), out k);
fonte
Outros deram a você a solução, mas por que isso é necessário: uma propriedade é apenas um açúcar sintático para um método .
Por exemplo, quando você declara uma propriedade chamada
Name
com um getter e um setter, o compilador realmente gera métodos chamadosget_Name()
eset_Name(value)
. Então, quando você lê e grava nessa propriedade, o compilador converte essas operações em chamadas para os métodos gerados.Quando você considera isso, fica óbvio por que você não pode passar uma propriedade como um parâmetro de saída - você na verdade estaria passando uma referência a um método , em vez de uma referência a
um objeto,uma variável , que é o que um parâmetro de saída espera.Um caso semelhante existe para indexadores.
fonte
Este é um caso de abstração que vaza. Uma propriedade é na verdade um método, os acessadores get e set para um indexador são compilados para os métodos get_Index () e set_Index. O compilador faz um excelente trabalho escondendo esse fato, ele traduz automaticamente uma atribuição a uma propriedade para o método set_Xxx () correspondente, por exemplo.
Mas isso vai de barriga para cima quando você passa um parâmetro de método por referência. Isso requer que o compilador JIT passe um ponteiro para a localização da memória do argumento passado. O problema é que, não existe um, atribuir o valor de uma propriedade requer chamar o método setter. O método chamado não pode dizer a diferença entre uma variável passada e uma propriedade passada e, portanto, não pode saber se uma chamada de método é necessária.
Notável é que isso realmente funciona em VB.NET. Por exemplo:
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
O compilador VB.NET resolve isso gerando automaticamente este código para o método Run, expresso em C #:
int temp = Prop; Test(ref temp); Prop = temp;
Essa é a solução alternativa que você também pode usar. Não tenho certeza de por que a equipe C # não usou a mesma abordagem. Possivelmente porque eles não queriam ocultar as chamadas getter e setter potencialmente caras. Ou o comportamento completamente não diagnosticável que você obterá quando o configurador tiver efeitos colaterais que alteram o valor da propriedade, eles desaparecerão após a atribuição. Diferença clássica entre C # e VB.NET, C # é "sem surpresas", VB.NET é "faça funcionar se você puder".
fonte
Coloque o parâmetro out em uma variável local e, em seguida, defina a variável em
bd.Budget
:double tempVar = 0.0; if (double.TryParse(objReader[i].ToString(), out tempVar)) { bd.Budget = tempVar; }
Atualização : Direto do MSDN:
fonte
Possivelmente de interesse - você pode escrever o seu próprio:
//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; }
fonte
Este é um post muito antigo, mas estou emendando o aceito, porque existe uma forma ainda mais conveniente de fazer isso que eu não conhecia.
É chamado de declaração inline e pode ter estado sempre disponível (como em instruções de uso) ou pode ter sido adicionado com C # 6.0 ou C # 7.0 para esses casos, não tenho certeza, mas funciona perfeitamente de qualquer maneira:
Inetad disso
double temp; double.TryParse(objReader[i].ToString(), out temp); bd.Budget = temp;
usa isto:
double.TryParse(objReader[i].ToString(), out double temp); bd.Budget = temp;
fonte
Então o orçamento é uma propriedade, correto?
Em vez disso, primeiro defina-o como uma variável local e, em seguida, defina o valor da propriedade para isso.
double t = 0; double.TryParse(objReader[i].ToString(), out t); bd.Budget = t;
fonte
Normalmente, quando tento fazer isso, é porque quero definir minha propriedade ou deixá-la com o valor padrão. Com a ajuda desta resposta e
dynamic
tipos, podemos criar facilmente um método de extensão de string para mantê-lo alinhado e simples.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); }
Use assim;
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
Em uma nota lateral, se for um,
SQLDataReader
você também pode fazer uso deGetSafeString
extensão (ões) para evitar exceções nulas do leitor.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; }
Use assim;
bd.Budget = objReader.GetSafeString(i).ParseAny(typeof(double)); bd.Budget = objReader.GetSafeString("ColumnName").ParseAny(typeof(double));
fonte