Generics IAbstract <T> наследуется от IAbstract

Я пытаюсь добиться чего-то подобного:

interface IAbstract
{
    string A { get; }
    object B { get; }
}

interface IAbstract<T> : IAbstract
{
    T B { get; }
}

class RealThing<T> : IAbstract<T>
{
    public string A { get; private set; }
    public T B { get; private set; }
}

Итак, я могу сделать что-то вроде этого:

RealThing<string> rt = new RealThing<string>();
IAbstract ia = rt;
IAbstract<string> ias = rt;
object o = ia.B;
string s = ias.B;

Возможно ли это?

Ответ 1

Очень близко. Три вещи:

  • Вы должны использовать new в IAbstract<T>, чтобы указать, что вы знаете, что скрываете существующего участника:

    new T B { get; }
    

    Но даже без этого вы все равно получите предупреждение.

  • Вам нужно реализовать IAbstract.B внутри RealThing, который вы почти наверняка будете делать, используя явную реализацию интерфейса, делегируя строго типизированному члену:

    object IAbstract.B { get { return B; } }
    
  • В вашем тестовом коде вам нужно указать аргумент типа для RealThing:

    RealThing<string> rt = new RealThing<string>();
    

Это хорошо и даже достаточно распространенный шаблон времени, когда вы хотите получить не-общую форму интерфейса.

Ответ 2

Да, с небольшими изменениями

interface IAbstract
{
    string A { get; }
    object B { get; }
}

interface IAbstract<T> : IAbstract
{
    new T B { get; }
}

sealed class RealThing<T> : IAbstract<T>
{
    public string A { get; private set; }
    public T B { get; private set; }

    object IAbstract.B
    {
        get { return B; }
    }
}

чтобы вы могли написать

var rt = new RealThing<string>();
IAbstract ia = rt;
IAbstract<string> ias = rt;
object o = ia.B;
string s = ias.B;

Ответ 3

Фактически интерфейсы System.Collections.IEnumerator и System.Collections.IEnumerator<T> делают это. Когда вы реализуете IEnumerable<T>, вам нужно будет реализовать один из свойств Current явно, обычно вы выбираете для него не общее:

object IEnumerable.Current
{
    // this calls the implicitly implemented generic property
    get { return this.Current; }
}

public T Current
{
    get { return this.current; } // or however you want to do it
}

Ответ 4

В этом случае вам даже не нужны два интерфейса. Просто пометьте интерфейс как ковариантный (поддерживается с С# 4):

interface IAbstract<out T>
{
    string A { get; }
    T B { get; }
}

и используйте IAbstract<object>, где раньше вы использовали не общий интерфейс.