En C #, la outpalabra clave se puede usar de dos maneras diferentes.
Como modificador de parámetros en el que se pasa un argumento por referencia
class OutExample { static void Method(out int i) { i = 44; } static void Main() { int value; Method(out value); // value is now 44 } }Como modificador de parámetros de tipo para especificar la covarianza .
// Covariant interface. interface ICovariant<out R> { } // Extending covariant interface. interface IExtCovariant<out R> : ICovariant<R> { } // Implementing covariant interface. class Sample<R> : ICovariant<R> { } class Program { static void Test() { ICovariant<Object> iobj = new Sample<Object>(); ICovariant<String> istr = new Sample<String>(); // You can assign istr to iobj because // the ICovariant interface is covariant. iobj = istr; } }
Mi pregunta es: ¿por qué?
Para un principiante, la conexión entre los dos no parece intuitiva . El uso con genéricos no parece tener nada que ver con pasar por referencia.
Primero aprendí lo que outestaba relacionado con pasar argumentos por referencia, y esto dificultó mi comprensión del uso de la definición de covarianza con genéricos.
¿Hay una conexión entre estos usos que me falta?
c#
terminology
language-design
keywords
Rowan Freeman
fuente
fuente

System.Func<in T, out TResult>delegado .Respuestas:
Hay una conexión, sin embargo, es un poco flojo. En C #, las palabras clave ´in´ y ´out´ como su nombre sugieren significan entrada y salida. Esto es muy claro en el caso de los parámetros de salida, pero es menos claro lo que tiene que ver con los parámetros de la plantilla.
Echemos un vistazo al principio de sustitución de Liskov :
¿Ves cómo la contravarianza está asociada con la entrada y la covarianza está asociada con la salida? En C #, si
outmarca una variable de plantilla para hacerla covariante, pero tenga en cuenta que solo puede hacer esto si el parámetro de tipo mencionado solo aparece como salida (tipo de retorno de función). Entonces lo siguiente no es válido:De manera similar si marca un parámetro de tipo con
in, eso significa que solo puede usarlo como entrada (parámetro de función). Entonces lo siguiente no es válido:Para resumir, la conexión con la
outpalabra clave es que con los parámetros de función significa que es un parámetro de salida , y para los parámetros de tipo significa que el tipo solo se usa en el contexto de salida .System.FuncTambién es un buen ejemplo de lo que Rwong mencionó en su comentario. EnSystem.Functodos los parámetros de entrada ar se ablandan conin, y el parámetro de salida se ablanda conout. La razón es exactamente lo que describí.fuente
@ Gábor ya ha explicado la conexión (contravarianza para todo lo que entra ", covarianza para todo lo que sale"), pero ¿por qué reutilizar las palabras clave?
Bueno, las palabras clave son muy caras. No puede usarlos como identificadores en sus programas. Pero hay pocas palabras en el idioma inglés. Entonces, a veces te encuentras con conflictos, y tienes que cambiar el nombre de tus variables, métodos, campos, propiedades, clases, interfaces o estructuras para evitar chocar con una palabra clave. Por ejemplo, si estás modelando una escuela, ¿cómo llamas a una clase? No se puede llamar una clase, porque
classes una palabra clave!Agregar una palabra clave a un idioma es aún más costoso. Básicamente, hace que todo el código que usa esta palabra clave como identificador sea ilegal, rompiendo la compatibilidad con versiones anteriores en todo el lugar.
Las palabras clave
inyoutya existían, por lo que podrían reutilizarse.Ellos podrían añadió claves contextuales que sólo son palabras clave en el contexto de una lista de parámetros de tipo, pero ¿qué palabras clave se han elegido?
covariantycontravariant?+y-(como hizo Scala, por ejemplo)?superyextendscomo lo hizo Java? ¿Podría recordar desde la parte superior de su cabeza qué parámetros son covariantes y contravariantes?Con la solución actual, hay una buena mnemotecnia: los parámetros de tipo de salida obtienen la
outpalabra clave, los parámetros de tipo de entrada obtienen lainpalabra clave. Tenga en cuenta la buena simetría con los parámetros del método: los parámetros de salida obtienen laoutpalabra clave, los parámetros de entrada obtienen lainpalabra clave (bueno, en realidad, no hay ninguna palabra clave en absoluto, ya que la entrada es la predeterminada, pero se entiende la idea).[Nota: si miras el historial de edición, verás que en realidad cambié los dos en mi oración introductoria. ¡Y hasta recibí un voto positivo durante ese tiempo! Esto solo muestra lo importante que es realmente la mnemotecnia.]
fuente
interface Accepter<in T> { void Accept(T it);};, unAccepter<Foo<T>>aceptaráTcomo un parámetro de entrada si loFoo<T>acepta como un parámetro de salida, y viceversa. Por lo tanto, contra -variance. Por el contrario,interface ISupplier<out T> { T get();};unaSupplier<Foo<T>>tendrá cualquier tipo de variaciónFootiene - por lo tanto co -variance.