En C #, la out
palabra 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 out
estaba 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
out
marca 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
out
palabra 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.Func
También es un buen ejemplo de lo que Rwong mencionó en su comentario. EnSystem.Func
todos 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
class
es 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
in
yout
ya 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?
covariant
ycontravariant
?+
y-
(como hizo Scala, por ejemplo)?super
yextends
como 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
out
palabra clave, los parámetros de tipo de entrada obtienen lain
palabra clave. Tenga en cuenta la buena simetría con los parámetros del método: los parámetros de salida obtienen laout
palabra clave, los parámetros de entrada obtienen lain
palabra 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áT
como 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ónFoo
tiene - por lo tanto co -variance.