En C # 4.0 pourquoi ne peut pas un paramètre dans une méthode être Covariant?
-
22-08-2019 - |
Question
Compte tenu de cette interface magique:
public interface IHat<out TRabbit>
{
TRabbit Take();
}
Et cette hiérarchie de classes:
public class Rabbit { }
public class WhiteRabbit : Rabbit { }
Je peux maintenant compiler ceci:
IHat<WhiteRabbit> hat1 = null;
IHat<Rabbit> hat2 = hat1;
Ce qui est génial. Mais si je définis différemment l'interface:
public interface IHat<out TRabbit>
{
bool Take(out TRabbit r);
}
J'indiquais que le chapeau peut être vide, en utilisant une valeur de retour booléen (la version précédente aurait peut-être retourné un lapin d'un chapeau nul vide). Mais je suis toujours sortie seulement un lapin, afin de ne pas faire quoi que ce soit logiquement différent à la version précédente.
Le compilateur C # 4.0 dans le CTP donne une erreur dans la définition de l'interface - il des paramètres de la méthode nécessite « out » pour être d'un type invariant. Y at-il une raison difficile et rapide pourquoi ce n'est pas autorisé, ou est-ce quelque chose qui pourrait être abordée dans une future version?
La solution
. Cependant, au niveau de la CLI il n'y a pas une telle chose comme « out » - seulement « ref »; il est un attribut qui aide les compilateurs (pour l'affectation définitive) qui dit « vous n'avez pas besoin de passer dans ».
Peut-être que cette restriction est parce que la CLI ne pas "out", seulement "ref".
Autres conseils
Bien qu'il soit un peu embêtant, vous pouvez utiliser une enveloppe de covariance:
public class CovariantListWrapper<TOut, TIn> : IList<TOut> where TIn : TOut
{
IList<TIn> list;
public CovariantListWrapper(IList<TIn> list)
{
this.list = list;
}
public int IndexOf(TOut item)
{
// (not covariant but permitted)
return item is TIn ? list.IndexOf((TIn)item) : -1;
}
public TOut this[int index]
{
get { return list[index]; }
set { throw new InvalidOperationException(); }
}
public bool Contains(TOut item)
{
// (not covariant but permitted)
return item is TIn && list.Contains((TIn)item);
}
public void CopyTo(TOut[] array, int arrayIndex)
{
foreach (TOut t in this)
array[arrayIndex++] = t;
}
public int Count { get { return list.Count; } }
public bool IsReadOnly { get { return true; } }
public IEnumerator<TOut> GetEnumerator()
{
foreach (TIn t in list)
yield return t;
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
public void Insert(int index, TOut item) { throw new InvalidOperationException(); }
public void RemoveAt(int index) { throw new InvalidOperationException(); }
public void Add(TOut item) { throw new InvalidOperationException(); }
public void Clear() { throw new InvalidOperationException(); }
public bool Remove(TOut item) { throw new InvalidOperationException(); }
}
Cela vous permet de garder la collection comme il a été saisi et faites référence covariante sans créer une copie détachée, de sorte que les mises à jour à l'original sont visibles dans l'utilisation covariant. Exemple:
class CovarianceWrapperExample
{
class Person { }
class Employee : Person { }
void ProcessPeople(IList<Person> people) { /* ... */ }
void Foo()
{
List<Employee> employees = new List<Employee>();
// cannot do:
ProcessPeople(employees);
// can do:
ProcessPeople(new CovariantListWrapper<Person, Employee>(employees));
}
}