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".