Qual é a diferença entre <out T>
e <T>
? Por exemplo:
public interface IExample<out T>
{
...
}
vs.
public interface IExample<T>
{
...
}
Qual é a diferença entre <out T>
e <T>
? Por exemplo:
public interface IExample<out T>
{
...
}
vs.
public interface IExample<T>
{
...
}
Respostas:
A out
palavra-chave em genéricos é usada para indicar que o tipo T na interface é covariante. Consulte Covariância e contravariância para obter detalhes.
O exemplo clássico é IEnumerable<out T>
. Como IEnumerable<out T>
é covariante, você pode fazer o seguinte:
IEnumerable<string> strings = new List<string>();
IEnumerable<object> objects = strings;
A segunda linha acima falharia se isso não fosse covariante, mesmo que logicamente funcione, pois a string deriva do objeto. Antes de a variação nas interfaces genéricas ser adicionada ao C # e ao VB.NET (no .NET 4 com VS 2010), era um erro de tempo de compilação.
Após o .NET 4, IEnumerable<T>
foi marcado covariante e tornou-se IEnumerable<out T>
. Como IEnumerable<out T>
apenas usa os elementos contidos nele e nunca os adiciona / altera, é seguro tratar uma coleção enumerável de strings como uma coleção enumerável de objetos, o que significa que é covariante .
Isso não funcionaria com um tipo como IList<T>
, pois IList<T>
possui um Add
método Suponha que isso seja permitido:
IList<string> strings = new List<string>();
IList<object> objects = strings; // NOTE: Fails at compile time
Você pode ligar para:
objects.Add(new Image()); // This should work, since IList<object> should let us add **any** object
Obviamente, isso falharia - portanto, IList<T>
não pode ser marcado como covariante.
Também existe uma opção para in
- que é usada por coisas como interfaces de comparação. IComparer<in T>
, por exemplo, funciona da maneira oposta. Você pode usar um concreto IComparer<Foo>
diretamente como um IComparer<Bar>
if Bar
é uma subclasse de Foo
, porque a IComparer<in T>
interface é contravariante .
Image
é uma classe abstrata;) Você pode fazer new List<object>() { Image.FromFile("test.jpg") };
sem problemas ou new List<object>() { new Bitmap("test.jpg") };
também. O problema com o seu é que new Image()
não é permitido (você não pode fazer var img = new Image();
qualquer um)
IList<object>
é um exemplo bizarro, se você deseja object
s, não precisa de genéricos.
Para lembrar facilmente o uso in
e a out
palavra - chave (também covariância e contravariância), podemos imaginar a herança como quebra automática:
String : Object
Bar : Foo
considerar,
class Fruit {}
class Banana : Fruit {}
interface ICovariantSkinned<out T> {}
interface ISkinned<T> {}
e as funções,
void Peel(ISkinned<Fruit> skinned) { }
void Peel(ICovariantSkinned<Fruit> skinned) { }
A função que aceita ICovariantSkinned<Fruit>
poderá aceitar ICovariantSkinned<Fruit>
ou ICovariantSkinned<Bananna>
porque ICovariantSkinned<T>
é uma interface covariante e Banana
é um tipo deFruit
,
a função que aceita ISkinned<Fruit>
somente poderá aceitar ISkinned<Fruit>
.
" out T
" significa que o tipo T
é "covariante". Isso restringe T
a aparecer apenas como um valor retornado (de saída) nos métodos da classe, interface ou método genérico. A implicação é que você pode converter o tipo / interface / método em um equivalente com um supertipo de T
.
Por exemplo, ICovariant<out Dog>
pode ser lançado para ICovariant<Animal>
.
out
reforças que T
podem ser retornadas apenas, até que eu li esta resposta. Todo o conceito faz mais sentido agora!
No link que você postou ....
Para parâmetros de tipo genérico, a palavra-chave out especifica que o parâmetro type é covariante .
EDIT : novamente, a partir do link que você postou
Para obter mais informações, consulte Covariância e contravariância (C # e Visual Basic). http://msdn.microsoft.com/en-us/library/ee207183.aspx