#realJSOP

Почему все ваши занятия абстрактны? В самом деле, почему все они абстрактны?

EADever

Потому что есть много классов класса эти абстрактные члены класса переопределяют

1 Ответов

Рейтинг:
10

Richard Deeming

Проблема в том, что универсальные типы по умолчанию инвариантны:
Ковариация и контравариантность в дженериках[^]
C# 4.0: Ковариация И Контравариантность В Дженериках[^]

Вы можете упростить пример:

public class A { }
public class B : A { }
public class Wrapper<T> where T : A { }

Wrapper<B> bWrapper = new Wrapper<B>();
Wrapper<A> aWrapper = bWrapper; // <-- Compiler error

Универсальными классами являются всегда инвариантный. Однако универсальные интерфейсы и делегаты могут быть инвариантными, ковариантными или контравариантными для каждого параметра типа. (Различные параметры типа могут иметь различное поведение, хотя это иногда может привести к путанице.)

Инвариантный это означает, что вы не можете рассматривать закрытый универсальный тип с одним параметром типа как один и тот же универсальный тип с другим параметром типа, независимо от каких-либо отношений наследования / реализации между двумя типами параметров. IWrapper<A> и IWrapper<B> это совершенно разные типы, между которыми нет никакой связи.

Ковариантный означает, что вы можете лечить экземпляр IWrapper<B> в качестве примера IWrapper<A>, до тех пор, пока B наследует / реализует A.

Чтобы что-то было ковариантный, параметр type может использоваться только в возвращаемом типе метода или типе свойства только для чтения.
public interface IWrapper<out T>
{
    T MyValue { get; }
    IWrapper<T> NextValue();
}

IWrapper<B> bWrapper = ...;
IWrapper<A> aWrapper = bWrapper;


Контравариантный означает, что вы можете лечить экземпляр IWrapper<A> в качестве примера IWrapper<B>, до тех пор, пока B наследует / реализует A. (Это называется "contra", потому что отношение между закрытыми универсальными типами течет в противоположном направлении к отношению между параметрами типа.)

Чтобы что-то было контравариантный, параметр type может использоваться только в типе входного параметра (не украшен ref или out), или тип свойства только для записи.
public interface IWrapper<int T>
{
    T Minimum { set; }
    T Maximum { set; }
    bool Contains(T value);
}

IWrapper<A> aWrapper = ...;
IWrapper<B> bWrapper = aWrapper;

Если параметр типа относится к обеим категориям, универсальный тип должен быть инвариантным для этого параметра.


В вашем примере вам нужен контроллер для A быть ковариантным. Предполагая, что вы используете параметр type только разрешенными способами, вам просто нужно извлечь интерфейс:
public interface IAController<out T> where T : A
{
}

public abstract class AController<T> : IAController<T> where T : A
{
}

public abstract class BController<T> : AController<T> where T : B
{
}

public abstract class A
{
    IAController<A> controller;
    
    public A(IAController<A> controller)
    {
        this.controller = controller;
    }
}

public abstract class B : A
{
    public B(BController<B> controller) : base(controller)
    {
    }
}

NB: Если вы используете параметр type для AController в позиции ввода вы получите ошибку компилятора.


EADever

Вау... это полностью руководство, и это то, что я ищу. Спасибо Вам за ваше предложение