2011-11-29 14 views
25

ReSharper ha sugerido cambiar de¿Qué significa <en TFrom, out TTo>?

interface IModelMapper<TFrom, TTo> 
{ 
    TTo Map(TFrom input); 
} 

en

interface IModelMapper<in TFrom, out TTo> 

Así que investigar un poco y terminó la lectura this article (que se encuentra a través de un artículoWikipedia) y un poco más de Google.

Todavía no estoy seguro de lo que esto implicaría para mi aplicación, por lo que estoy tentado de no aceptar la sugerencia. ¿Cuáles serían los beneficios que este cambio introduciría y no estoy considerando ignorar la sugerencia?

Más explícitamente, ¿por qué debería aceptarlo?

Respuesta

48

línea de base: ReSharper ha investigado su tipo, y descubrió que TFrom puede usarse contravariantly y TTo covariantly. La aceptación del refactor le permitirá usar estos tipos con mayor flexibilidad, como se describe a continuación. Si eso puede ser útil para usted, acéptelo.

Tenga en cuenta, sin embargo, que al aceptar este refactorio, se aplican restricciones sobre cómo utilizar estos tipos en el futuro. Si alguna vez escribe un método que toma TTo como parámetro, obtendrá un error de compilación, ya que no se pueden leer los tipos coviariant. Y lo mismo para TFrom: nunca podrá tener un método que devuelva este tipo, o tiene un parámetro out de este tipo.


que le está diciendo que es TFrom contravariant, y que TTo es covariante. Estas características fueron recientemente added to C#

Tipo covarianza significa que un más tipo específico se puede hacer pasar en, mientras contravarianza significa que un menos tipo específico se puede hacer pasar en.

IEnumerable<T> es un buen ejemplo de Tipo covarianza. Dado que los elementos de un IEnumerable<T> se sólo lectura, es posible configurarlo para algo más específico:

IEnumerable<object> objects = new List<string>(); 

Considere lo que podría suceder si (hipotéticamente) se dejen de hacer esto para las colecciones que fueron de lectura/escritura :

List<object> objects = new List<string>(); 
objects.Add(new Car()); 
//runtime exception 

Para pertenecer covariante, un parámetro genérico debe ser utilizado en una de sólo lectura manera estrictamente ; solo debe escribirse del tipo y nunca leer en (de ahí las palabras clave). Es por eso que el ejemplo IEnumerable<T> funciona, pero el ejemplo List<T> no. Por cierto, las matrices hacen covarianza de tipo de soporte (ya que Java, creo), por lo que este mismo tipo de error de tiempo de ejecución es posible con las matrices.

La contravarianza de tipo significa todo lo contrario.Para admitir la contravariancia de tipo, un parámetro genérico debe leerse en solamente, y nunca debe escribirse en. Esto le permite sustituir los tipos menos específicos en

Action<T> es un ejemplo de tipo contravaince:.

Action<object> objAction = (o => Console.WriteLine(o.ToString())); 
Action<string> strAction = objAction; 
strAction("Hello"); 

strAction se declara a tomar un parámetro de cadena, pero funciona bien si sustituye un tipo de objeto. Se pasará una cadena, pero si el delegado que está configurado para trabajar con ella elige tratarla como un objeto, entonces que así sea. Ningún daño hecho.

Para completar, Func<T> es el caso inverso de Action<T>; T aquí solamente se devuelve, por lo tanto es covariante:

Func<string> strDelegate = () => "Hello"; 
Func<object> myObjFunc = strDelegate; 
object O = myObjFunc(); 

myObjectFunc se codifica para devolver un objeto. Si lo configura en algo que devuelve una cadena, entonces, de nuevo, no se hace daño.

+0

gracias, es realmente útil. – mhttk

+0

@mhttk - genial. Me alegro de ayudar –

+0

Probablemente la mejor explicación de co/contravarianza que he leído - ¡gracias! –

2

Como un ejemplo de cómo esta elección podría afectar a su aplicación, suponga que tiene un tipo CustomerAddressMapper que implementa IModelMapper<Customer, Address[]> y otro SupplierAddressMapper que implementa IModelMapper<Supplier, Address[]>. Los tipos Customer y Supplier comparten una clase base Company, pero su lógica de dirección es distinta, por lo que necesitamos tipos separados para manejar esto.

Ahora suponga que tiene un método que toma un IMapper<Company, Address[]>. Antes de la contravariancia de la interfaz, no podría pasar instancias de CustomerAddressMapper o SupplierAddressMapper a este método. Ahora, si usa el modificador in en el parámetro de tipo TFrom, puede hacerlo.

Por otra parte, a causa de covarianza en el parámetro TTo, también puede pasar una CustomerAddressMapper a métodos que requieren una IMapper<Customer, object>.