Допустимо ли скрывать свойство в производном интерфейсе, которое возвращает производный тип?

TL;DR

Что плохого в том, чтобы скрыть свойство в интерфейсе, чтобы я мог изменить его объявление, чтобы оно возвращало производный тип исходного свойства?

Я уверен, что об этом уже спрашивали, но я не могу его найти, и извиняюсь за длинный вопрос.

Скажем, у меня есть такая ситуация:

public interface A
{
    B TheB{get;}
}

public interface MoreSpecificA : A
{
    MoreSpecificB TheMoreSpecificB{get;}
}

public interface B{...}

public interface MoreSpecificB:B{...}

Я хотел бы, чтобы пользователи MoreSpecificA чтобы иметь возможность добраться до B, который является MoreSpecificB, Они могли бы сделать это, позвонив TheB и бросить его, или они могли бы вызвать метод TheMoreSpecificB, Я также мог бы заявить MoreSpecificA вот так:

public interface MoreSpecificA : A
{
    new MoreSpecificB TheB{get;}
}

так что теперь они могут просто использовать тот же метод и получить обратно MoreSpecificB,

С использованием new Спрятать метод заставляет мои зубы нервничать, так почему это плохая идея? Это кажется разумным, чтобы сделать здесь.

Общее предложение в большинстве случаев, которое я видел для этого, заключается в том, чтобы вместо этого использовать дженерики, но, похоже, проблема заключается в том, что если у меня MoreSpecificA и я хочу вернуть его в методе, который объявляет тип возвращаемого значения как A тогда я должен иметь MoreSpecificA простираться A что дает двусмысленность при доступе TheB на MoreSpecificA экземпляр, как он не знает, если вы хотите A.TheB или же MoreSpecificA.TheB

public interface ABase<T> where T : B
{
    T TheB{get;}
}

public interface A : ABase<B>
{        
}

public interface MoreSpecificA : ABase<MoreSpecificB>,A
{        
}

public class blah
{
    public A GetA(MoreSpecificA specificA)
    {
        return specificA; //can't do this unless MoreSpecificA extends A 
    }

    public B GetB(MoreSpecificA specificA)
    {
        return specificA.TheB; //compiler complains about ambiguity here, if MoreSpcificA extends A
    }
}

которая может быть решена путем объявления нового TheB на MoreSpecificA (но новый вопрос снова).

Если MoreSpecificA не расширяет A, тогда первый метод в классе blah выше жалуется, так как теперь MoreSpcificA не может быть преобразован в A.

При написании этого я заметил, что если я объявлю, что моя BaseA является контрвариантной, вот так:

public interface ABase<out T> where T : B
{
    T TheB{get;}
}

и мой класс будет

public class blah
{
    public ABase<B> GetA(MoreSpecificA specificA)
    {
        return specificA; 
    }

    public B GetB(MoreSpecificA specificA)
    {
        return specificA.TheB; //compiler complains about ambiguity here
    }
}

Тогда я получаю лучшее из обоих миров. Применимость этого решения зависит от того, A добавляет что-нибудь к ABase?

Или мой первоначальный план - просто скрыть метод в производном типе, чтобы вернуть производный тип исходного метода?

1 ответ

Решение

Или мой первоначальный план - просто скрыть метод в производном типе, чтобы вернуть производный тип исходного метода?

Пока это означает одно и то же, я думаю, что все в порядке. Вы можете увидеть что-то подобное в стандартных библиотеках, с IDbConnection.CreateCommand (который возвращает IDbCommand) а также SqlConnection.CreateCommand (который возвращает SqlCommand) например.

В этом случае он использует явную реализацию интерфейса для IDbConnection версия, но это тот же принцип.

Вы также можете увидеть это в IEnumerator<T>.Current против IEnumerator.Current а также IEnumerable<T>.GetEnumerator() против IEnumerable.GetEnumerator(),

Я бы использовал его только в тех случаях, когда реализация метода с более слабой типизацией просто возвращает результат вызова метода с более строгой типизацией, хотя и использует неявное преобразование. Когда они действительно начинают делать разные вещи, об этом становится гораздо сложнее рассуждать позже.

Другие вопросы по тегам