Допустимо ли скрывать свойство в производном интерфейсе, которое возвращает производный тип? - PullRequest
2 голосов
/ 20 марта 2012

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 extend 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 Ответ

2 голосов
/ 20 марта 2012

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

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

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

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

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

...