« Variance » : différence entre les versions

De Banane Atomic
Aller à la navigationAller à la recherche
 
(22 versions intermédiaires par le même utilisateur non affichées)
Ligne 5 : Ligne 5 :


= [https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/covariance-contravariance Description] =
= [https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/covariance-contravariance Description] =
Covariance enable implicit reference conversion for array types, delegate types, and generic type arguments.<br>
Variance allows to replace a type with a less-derived (covariance: derived → base) type or a more-derived type (contravariance: base → derived).<br>
Covariance preserves assignment compatibility and contravariance reverses it.
It is available for array types, delegate types, and generic types.
{{info | Since .NET Framework 4, C# supports covariance and contravariance in generic interfaces and delegates and allows for implicit conversion of generic type parameters.}}


= Assignment compatibility =
= Assignment compatibility =
Allow an object of a more derived type (child class, ex: string) to be assigned to an object of a less derived type (parent class, ex: object).
Allow an object of a more derived type (derived) to be assigned to an object of a less derived type (base).
<kode lang='cs'>
<kode lang='cs'>
string s = "test";
Derived derived;
object o = s;
Base base = derived;
</kode>
</kode>


= Covariance =
= Covariance =
Allow a generic object of child class type (ex: string) to be assigned to a generic object of parent class type (ex: object).
Allow a generic object with derived type to be assigned to a generic object of base type.
{{info | It works only with covariant interface}}
{{info | It works only with covariant interface}}
<kode lang='cs'>
<kode lang='cs'>
var strings = new List<string>();
var deriveds = new List<Derived>();
List<object> objects = strings;  // Cannot convert type List<string> to List<object> because there is no covariance with List<T>
List<Base> bases = strings;  // Cannot convert type List<Base> to List<Derived> because List<T> is invariant
IList<object> objects = strings;  // Cannot implicitly convert type List<string> to IList<object>. An explicit conversion exists
IList<Base> bases = strings;  // Cannot implicitly convert type List<Base> to IList<Derived> because IList<T> is invariant
var bases = (IList<Base>)deriveds; // at execution, Unable to cast List<Derived> to type IList<Base>


IEnumerable<object> objects = strings; // There is covariance with IEnumerable<T>
IEnumerable<Base> bases = deriveds;         // IEnumerable<T> is covariant
IReadOnlyList<Base> bases = deriveds;      // IReadOnlyList<T> is covariant
IReadOnlyCollection<Base> bases = deriveds; // IReadOnlyCollection<T> is covariant
</kode>
 
== Create a covariant interface ==
<kode lang='cs'>
var derivedVariants = new VariantList<Derived> { new Derived() };
IVariantList<Base> baseVariants = derivedVariants;
 
var derivedVariant = new Variant<Derived>();
IVariant<Base> baseVariant = derivedVariant;


var objects = (IList<object>)strings; // There is covariance with IList<T>
interface IVariantList<out T> { }
class VariantList<T> : List<T>, IVariantList<T> { }
 
interface IVariant<out T> { }
class Variant<T> : IVariant<T> { }
</kode>
</kode>
= Contravariance =
Allow a generic object with base type to be used for a generic object of derived type.
<kode lang='cs'>
// an equality comparer for Base type, used with Derived objects
class BaseComparer : IEqualityComparer<Base>
{
    public bool Equals(Base? x, Base? y) => x?.Id == y?.Id;
    public int GetHashCode(Base obj) => obj.Id;
}
var derived1 = new Derived { Id = 1 };
var derived1bis = new Derived { Id = 1 };
var derived2 = new Derived { Id = 2 };
var baseComparer = new BaseComparer();
var b3 = baseComparer.Equals(derived1, derived1bis);
var b4 = baseComparer.Equals(derived1, derived2);
</kode>
= [https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/covariance-contravariance/variance-in-generic-interfaces Native variant interfaces] =
{| class="wikitable wtp wtmono1"
! Interface
! Since
|-
| IEnumerable<out T> || .NET Framework 4.0
|-
| IReadOnlyList<out T> || .NET Framework 4.5
|-
| IReadOnlyCollection<out T> || .NET Framework 4.5
|}

Dernière version du 27 mars 2024 à 16:15

Links

Description

Variance allows to replace a type with a less-derived (covariance: derived → base) type or a more-derived type (contravariance: base → derived).
It is available for array types, delegate types, and generic types.

Assignment compatibility

Allow an object of a more derived type (derived) to be assigned to an object of a less derived type (base).

Cs.svg
Derived derived;
Base base = derived;

Covariance

Allow a generic object with derived type to be assigned to a generic object of base type.

It works only with covariant interface
Cs.svg
var deriveds = new List<Derived>();
List<Base> bases = strings;   // Cannot convert type List<Base> to List<Derived> because List<T> is invariant
IList<Base> bases = strings;  // Cannot implicitly convert type List<Base> to IList<Derived> because IList<T> is invariant
var bases = (IList<Base>)deriveds; // at execution, Unable to cast List<Derived> to type IList<Base>

IEnumerable<Base> bases = deriveds;         // IEnumerable<T> is covariant
IReadOnlyList<Base> bases = deriveds;       // IReadOnlyList<T> is covariant
IReadOnlyCollection<Base> bases = deriveds; // IReadOnlyCollection<T> is covariant

Create a covariant interface

Cs.svg
var derivedVariants = new VariantList<Derived> { new Derived() };
IVariantList<Base> baseVariants = derivedVariants;

var derivedVariant = new Variant<Derived>();
IVariant<Base> baseVariant = derivedVariant;

interface IVariantList<out T> { }
class VariantList<T> : List<T>, IVariantList<T> { }

interface IVariant<out T> { }
class Variant<T> : IVariant<T> { }

Contravariance

Allow a generic object with base type to be used for a generic object of derived type.

Cs.svg
// an equality comparer for Base type, used with Derived objects
class BaseComparer : IEqualityComparer<Base>
{
    public bool Equals(Base? x, Base? y) => x?.Id == y?.Id;
    public int GetHashCode(Base obj) => obj.Id;
}

var derived1 = new Derived { Id = 1 };
var derived1bis = new Derived { Id = 1 };
var derived2 = new Derived { Id = 2 };

var baseComparer = new BaseComparer();
var b3 = baseComparer.Equals(derived1, derived1bis);
var b4 = baseComparer.Equals(derived1, derived2);

Native variant interfaces

Interface Since
IEnumerable<out T> .NET Framework 4.0
IReadOnlyList<out T> .NET Framework 4.5
IReadOnlyCollection<out T> .NET Framework 4.5