C# 4.0에서 왜 메소드의 Out 매개 변수가 공분산이 될 수 없습니까?

StackOverflow https://stackoverflow.com/questions/527758

  •  22-08-2019
  •  | 
  •  

문제

이 마법의 인터페이스를 감안할 때 :

public interface IHat<out TRabbit>
{
    TRabbit Take();
}

그리고이 클래스 계층 구조 :

public class Rabbit { }

public class WhiteRabbit : Rabbit { }

이제 이것을 컴파일 할 수 있습니다.

IHat<WhiteRabbit> hat1 = null;
IHat<Rabbit> hat2 = hat1;

그것은 훌륭합니다. 그러나 인터페이스를 다르게 정의하면 어떻게해야합니까?

public interface IHat<out TRabbit>
{
    bool Take(out TRabbit r);
}

별도의 부울 리턴 값을 사용하여 모자가 비어있을 수 있음을 나타냅니다 (이전 버전은 아마도 빈 모자에서 널 토끼를 반환했을 것입니다). 그러나 나는 여전히 토끼 만 출력하고 있으므로 이전 버전과 논리적으로 다른 일을하지 않습니다.

CTP의 C# 4.0 컴파일러는 인터페이스 정의에서 오류를 제공합니다. 이것이 허용되지 않는 이유가 어려운 이유가 있습니까?

도움이 되었습니까?

해결책

흥미로운. 그러나 CLI 수준에서는 "out" - "Ref"와 같은 것은 없습니다. 컴파일러 (명확한 할당의 경우)를 도와주는 속성이 있습니다.

어쩌면이 제한은 CLI에 "아웃"이없고 "Ref"만 가지고 있기 때문일 수 있습니다.

다른 팁

약간의 번거 로움이지만 공분산 래퍼를 사용할 수 있습니다.

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(); }
}

이를 통해 원래 입력 된대로 컬렉션을 유지하고 분리 된 사본을 만들지 않고 공분산으로 참조 할 수 있으므로 원본에 대한 업데이트가 공변량 사용에서 볼 수 있습니다. 예시:

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));
    }
}
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top