Ковариантность / Contravariance Conundrum при использовании общих ограничений интерфейса - PullRequest
3 голосов
/ 25 мая 2011
    public interface IShape{}

    public class Rectangle : IShape{}

    public class Base{}

    public class Derived : Base{}

    public interface IFoo<out T, in U>
        where T : IShape
        where U : Base
    {
        T Convert(U myType);
    }

    public class MyFoo : IFoo<Rectangle, Derived>
    {
        public Rectangle Convert(Derived myType)
        {
            throw new NotImplementedException();
        }
    }    

    class Program
    {
        static void Main(string[] args)
        {
            IFoo<IShape, Base> hmm = new MyFoo();
        }
    }

Учитывая приведенный выше код, компилятор не может определить, как назначить тип MyFoo для IFoo<IShape, Base>, предположительно потому, что U установлен как выход, означающий, что он может принимать меньше производных.Тем не менее, Derived, скорее, более производный, чем Base, поэтому генерирует ошибку компилятора.

Этот пример надуманный, но мы имеем дело с реализацией, в которой MyFoo будет возвращено изфабрика.

Хотя U используется в качестве параметра, он также выводится при попытке назначить его универсальному интерфейсу, но я не могу использовать здесь ключевое слово out.Как мы могли бы обойти это?

Ответы [ 2 ]

1 голос
/ 25 мая 2011

Ваш интерфейс IFoo кажется неправильным в этом использовании, он должен быть:

public interface IFoo<out T, **out** U>

С U вне. Помните, что параметр общего типа out означает, что он может изменяться «наружу». То есть вы можете неявно расширить тип до более широкого типа. In, однако, означает, что вы можете неявно сузить тип «внутрь» к более конкретному типу. Это, конечно, грубые аналогии.

Таким образом, в случае присвоения hmm вы неявно пытаетесь расширить параметр универсального типа интерфейса для U с Derived до Base, но интерфейс объявляет его сужающимся (in):

IFoo<IShape, Base> hmm = new MyFoo();

Так что он не может сделать неявное преобразование. Если вы действительно хотите неявно расширять этот интерфейс, аргумент второго типа должен быть out вместо in.

Обновление: после ваших комментариев я вижу, что большая дилемма состоит в том, что вы хотите, чтобы он был как входным, так и выходным, что на самом деле невозможно, так как это контравариантный ввод, вы не можете назначить интерфейс ковариантно IFoo<IShape, Base>, к несчастью.

Вам нужно либо кодировать тот факт, что вы не можете присвоить IFoo<IShape,Base>, либо создать Foo как:

public class MyFoo : IFoo<Rectangle, Base>

А затем приведите к Rectangle внутри реализации. Главное, чтобы у одного и того же параметра типа не было и ковариации, и контравариантности.

Имеет ли это смысл?

0 голосов
/ 25 мая 2011

Что-то, что может преобразовать Основу в Прямоугольник, также превратит Производное в IShape. Однако то, что может преобразовать Производное в Прямоугольник, может быть не в состоянии сделать что-либо полезное с Базой. Вы правильно определили, что спецификатор ковариации для вашего второго параметра должен быть "in", но затем пытаетесь использовать ковариацию способом, противоположным тому, что он на самом деле поддерживает.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...