Допустимо ли скрывать свойство в производном интерфейсе, которое возвращает производный тип?
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()
,
Я бы использовал его только в тех случаях, когда реализация метода с более слабой типизацией просто возвращает результат вызова метода с более строгой типизацией, хотя и использует неявное преобразование. Когда они действительно начинают делать разные вещи, об этом становится гораздо сложнее рассуждать позже.