본문 바로가기
Works/C#

Covariance and Contravariance

by Vader87 2022. 1. 19.
반응형

Covariance and Contravariance (C#)

용어 정리

Invariance Means that you can use only the type originally specified. An invariant generic type parameter is neither covariant nor contravariant.
You cannot assign an instance of List to a variable of type List or vice versa.
Variance Convariance Enables you to use a more derived type than originally specified.
You can assign an instance of IEnumerable to a variable of type IEnumerable.
Contravariance Enables you to use a more generic (less derived) type than originally specified.
You can assign an instance of Action to a variable of type Action.

 

in (Generic Modifier) (C# Reference)

Contravariance enables

  • IContravariance<ClassB> 의 SetValue 에서 GetA 와 GetB 를 사용할 수 있다.
  • IContravariance<ClassB> 를 IContravariance<ClassC> 로 conversion 후 SetValue 에서 GetA 와 GetB 를 사용할 수 있다.
  • IContravariance<ClassB> 를 IContravariance<ClassA> 로 conversion  후 SetValue 에서 GetA 는 사용할 수 있지만, GetB 를 사용하는 것은 문제가 될 수 있다.
  • 따라서 IContravariance<ClassB> 를 IContravariance<ClaassA> 로 conversion 하는 것은 허용하지 않는다.
class ClassA
{
	public void GetA() { }
}

class ClassB : ClassA
{
	public void GetB() { }
}

class ClassC : ClassB
{
	public void GetC() { }
}

interface IContravariance<in T>
{
	// T GetValue(); // CS1961: 잘못된 가변성(variance): 'T' 형식 매개 변수는 'IContravariance<T>.GetValue()' 에서 유효한 공변(covariant) 방식이어야 합니다. 'T'(은)는 반공변(contravariant)입니다.
	void SetValue(T t);
}

class CreateB : IContravariance<ClassB>
{
	public void SetValue(ClassB t) {  }
}

public void Run()
{
	IContravariance<ClassB> creatorB = new CreateB();
	IContravariance<ClassC> creatorC = creatorB;
	// IContravariance<ClassA> creatorA = creatorB;
}

 

out (generic modifier) (C# Reference)

Covariance enables

  • ICovariance<ClassB> 의 GetValue 값에서 GetA 와 GetB 를 사용할 수 있다.
  • ICovariance<ClassB> 를 ICovariance<ClassA> 로 conversion 후 GetValue 값에서 GetA 를 사용할 수 있다.
  • ICovariance<ClassB> 를 ICovariance<ClassC> 로 conversion후 GetValue 값에서 GetA 와 GetB 는 사용할 수 있지만, GetC 를 사용하는 것은 문제가 될 수 있다.
  • 따라서 ICovariance<ClassB> 를 ICovariance<ClassC> 로 conversion 하는 것은 허용하지 않는다.
class ClassA
{
	public void GetA() { }
}

class ClassB : ClassA
{
	public void GetB() { }
}

class ClassC : ClassB
{
	public void GetC() { }
}

interface ICovariance<out T>
{
	T GetValue();
    // void SetValue(T t); // CS1961:잘못된 가변성(variance): 'T' 형식 매개 변수는 'ICovariance<T>.SetValue(T)'에서 유효한 반공변(contravariant) 방식이어야 합니다. 'T'은(는) 공변(covariant)입니다.
}

class CreateB : ICovariance<ClassB>
{
	public ClassB GetValue() { return new ClassB(); }
}

public void Run()
{
	ICovariance<ClassB> creatorB = new CreateB();
	ICovariance<ClassA> creatorA = creatorB;
	// ICovariance<ClassC> creatorC = creatorB;
}

 

Variance in Generic Interfaces (C#)

Starting with .NET Framework 4, the following interfaces are variant:

IEnumerable<T> (T is covariant)

Why was IEnumerable made covariant in C# 4?

namespace System.Collections.Generic
{
    public interface IEnumerable<out T> : IEnumerable
    {
        IEnumerator<T> GetEnumerator();
    }
}

 

IEnumerator<T> (T is covariant)

namespace System.Collections.Generic
{
    public interface IEnumerator<out T> : IEnumerator, IDisposable
    {
        T Current { get; }
    }
}

 

IQueryable<T> (T is covariant)

using System.Collections;
using System.Collections.Generic;

namespace System.Linq
{
    public interface IQueryable<out T> : IEnumerable<T>, IEnumerable, IQueryable
    {
    }
}

 

IGrouping<TKey,TElement> (TKey and TElement are covariant)

using System.Collections;
using System.Collections.Generic;

namespace System.Linq
{
    public interface IGrouping<out TKey, out TElement> : IEnumerable<TElement>, IEnumerable
    {
        TKey Key { get; }
    }
}

 

IComparer<T> (T is contravariant)

namespace System.Collections.Generic
{
    public interface IComparer<in T>
    {
        int Compare(T x, T y);
    }
}

 

IEqualityComparer<T> (T is contravariant)

namespace System.Collections.Generic
{
    public interface IEqualityComparer<in T>
    {
        bool Equals(T x, T y);
        int GetHashCode(T obj);
    }
}

 

IComparable<T> (T is contravariant)

namespace System
{
    public interface IComparable<in T>
    {
        int CompareTo(T other);
    }
}

 

Starting with .NET Framework 4.5, the following interfaces are variant:

IReadOnlyList<T> (T is covariant)

namespace System.Collections.Generic
{
    [DefaultMember("Item")]
    public interface IReadOnlyList<out T> : IEnumerable<T>, IEnumerable, IReadOnlyCollection<T>
    {
        T this[int index] { get; }
    }
}

 

IReadOnlyCollection<T> (T is covariant)

namespace System.Collections.Generic
{
    public interface IReadOnlyCollection<out T> : IEnumerable<T>, IEnumerable
    {
        int Count { get; }
    }
}
반응형

'Works > C#' 카테고리의 다른 글

C# 암호화 변수  (0) 2020.09.23
[.NET] Windows Registry 등록/삭제  (0) 2019.09.08
[.NET] Java Bridge  (2) 2019.03.22
For vs Foreach  (0) 2018.12.04

댓글