=============
ACTUALIZACIÓN: Utilicé esta respuesta como base para esta entrada de blog:
¿Por qué los parámetros ref y out no permiten la variación de tipo?
Consulte la página del blog para obtener más comentarios sobre este tema. Gracias por la gran pregunta.
=============
Supongamos que usted tiene clases Animal, Mammal, Reptile, Giraffe, Turtley Tiger, con las relaciones de subclases obvias.
Ahora suponga que tiene un método void M(ref Mammal m). MPuede leer y escribir tanto m.
¿Se puede pasar una variable de tipo Animala M?
No. Esa variable podría contener a Turtle, pero Msupondrá que solo contiene mamíferos. A Turtleno es a Mammal.
Conclusión 1 : los refparámetros no pueden hacerse "más grandes". (Hay más animales que mamíferos, por lo que la variable se está haciendo "más grande" porque puede contener más cosas).
¿Se puede pasar una variable de tipo Giraffea M?
No. Mpuede escribir my Mpuede querer escribir un correo Tigerelectrónico m. Ahora ha puesto una Tigervariable en una variable que en realidad es de tipo Giraffe.
Conclusión 2 : los refparámetros no pueden hacerse "más pequeños".
Ahora considera N(out Mammal n).
¿Se puede pasar una variable de tipo Giraffea N?
No. Npuede escribir ny Npuede querer escribir a Tiger.
Conclusión 3 : los outparámetros no pueden hacerse "más pequeños".
¿Se puede pasar una variable de tipo Animala N?
Hmm
¿Bueno, por qué no? Nno puede leer n, solo puede escribirle, ¿verdad? Escribes un Tigera una variable de tipo Animaly estás listo, ¿verdad?
Incorrecto. La regla no es " Nsolo se puede escribir n".
Las reglas son, brevemente:
1) Ntiene que escribir nantes de que Nvuelva normalmente. (Si Nlanza, todas las apuestas están canceladas).
2) Ntiene que escribir algo nantes de que lea algo n.
Eso permite esta secuencia de eventos:
- Declarar un campo
xde tipo Animal.
- Pase
xcomo outparámetro a N.
Nescribe un Tigeren n, que es un alias para x.
- En otro hilo, alguien escribe un
Turtleen x.
Nintenta leer el contenido de n, y descubre un Turtleen lo que piensa que es una variable de tipo Mammal.
Claramente queremos que sea ilegal.
Conclusión 4 : los outparámetros no pueden hacerse "más grandes".
Conclusión final : Ni reftampoco outparámetros pueden variar sus tipos. Hacer lo contrario es romper la seguridad de tipo verificable.
Si estas cuestiones en la teoría básica de tipos le interesan, considere leer mi serie sobre cómo funcionan la covarianza y la contravarianza en C # 4.0 .
outparámetros no pueden hacerse "más grandes"? La secuencia que ha descrito se puede aplicar a cualquier variable, no solo a laoutvariable de parámetro. Y también las necesidades de los lectores para emitir el valor del argumento aMammalantes de intentar acceder a él comoMammaly por supuesto que puede fallar si no es consideradoPorque en ambos casos debe poder asignar un valor al parámetro ref / out.
Si intenta pasar b al método Foo2 como referencia, y en Foo2 intenta asignar a = new A (), esto sería inválido.
La misma razón por la que no puedes escribir:
fuente
Estás luchando con el clásico problema OOP de covarianza (y contravarianza), mira wikipedia : aunque este hecho puede desafiar las expectativas intuitivas, es matemáticamente imposible permitir la sustitución de clases derivadas en lugar de las bases por argumentos mutables (asignables) (y también contenedores cuyos artículos son asignables, por la misma razón) sin dejar de respetar el principio de Liskov . Por qué eso es así se esboza en las respuestas existentes y se explora más profundamente en estos artículos wiki y enlaces a partir de ellas.
Los lenguajes OOP que parecen hacerlo mientras permanecen tradicionalmente estáticamente seguros de tipos son "trampas" (insertando verificaciones de tipos dinámicos ocultos o requiriendo un examen en tiempo de compilación de TODAS las fuentes para verificar); la elección fundamental es: renunciar a esta covarianza y aceptar la perplejidad de los profesionales (como lo hace C # aquí), o pasar a un enfoque de tipeo dinámico (como lo hizo el primer lenguaje OOP, Smalltalk), o pasar a inmutable (single- asignación) datos, como lo hacen los lenguajes funcionales (bajo inmutabilidad, puede admitir covarianza y también evitar otros acertijos relacionados, como el hecho de que no puede tener Rectángulo cuadrado de subclase en un mundo de datos mutables).
fuente
Considerar:
Violaría la seguridad de tipos
fuente
varestuvo totalmente mal. Fijo.Si bien las otras respuestas han explicado sucintamente el razonamiento detrás de este comportamiento, creo que vale la pena mencionar que si realmente necesita hacer algo de esta naturaleza, puede lograr una funcionalidad similar al convertir Foo2 en un método genérico, como tal:
fuente
Porque dar
Foo2unref Bresultado daría como resultado un objeto mal formado porqueFoo2solo sabe cómo llenarAparte de élB.fuente
¿No es ese el compilador que le dice que le gustaría que expulse el objeto explícitamente para que pueda estar seguro de cuáles son sus intenciones?
fuente
Tiene sentido desde una perspectiva de seguridad, pero hubiera preferido que el compilador diera una advertencia en lugar de un error, ya que hay usos legítimos de objetos polimorfos pasados por referencia. p.ej
Esto no se compilará, pero ¿funcionaría?
fuente
Si usa ejemplos prácticos para sus tipos, lo verá:
Y ahora tienes tu función que toma el ancestro ( es decir
Object):¿Qué puede estar mal con eso?
Acabas de asignar una
Bitmapa tuSqlConnection.Eso no es bueno.
Intente nuevamente con otros:
Rellenaste
OracleConnectiondemasiadoSqlConnection.fuente
En mi caso, mi función aceptó un objeto y no pude enviar nada, así que simplemente hice
Y eso funciona
My Foo está en VB.NET y comprueba el tipo dentro y hace mucha lógica
Pido disculpas si mi respuesta es duplicada pero otras fueron demasiado largas
fuente