Преобразование из чего-то общего из реализации в нечто общее из типа интерфейса - PullRequest
2 голосов
/ 05 марта 2012

, вероятно, легко, но, пожалуйста, взгляните на следующие классы / интерфейсы:

public interface IChallenge
public interface IChallengeView<T> where T : IChallenge 
{
    T Challenge {get;set;}
}
public interface IChallengeHostView
{
    IChallengeView<IChallenge> ChallengeView { get; set; }
}

public class AdditionChallenge : IChallenge {}
public class AdditionChallengeView: IChallengeView<AdditionChallenge> {}

Сценарий представляет собой дидактическое приложение для детей младшего возраста.Я намерен сохранить гибкость приложения, отделяя хост (который может быть любым графическим окружением) от задачи, которая должна быть решена.Таким образом, я мог бы использовать одно и то же окружение для размещения сложения, умножения, деления ...

Теперь, когда я хочу заполнить это какой-то жизнью, у меня возникает проблема с преобразованием:

HostView hostView = new HostView();  // implements IChallengeHostView
AdditionChallengeView challengeView = new AdditionChallengeView();
hostView.ChallengeView = challengeView;

Это, конечно, не работает.Я понимаю, почему это не так, но я понятия не имею, как обойти это.

Есть идеи?

ОБНОВЛЕНИЕ : я решил опубликовать как можно меньше кодакак можно раньше, но это привело меня к проблеме сокрытия одной проблемы от вас, ребята: интерфейс IChallengeView имеет настраиваемое свойство (теперь видно в коде выше), что делает ковариациюздесь невозможно применить - параметр универсального типа может быть только инвариантным в этом случае.

Ответ, данный rich.okelly, является правильным, но основан на ложных предположениях (которые, опять же, были основаны на плохом уровнеподробности приведены в моем описании здесь).

Я решил сделать код немного менее адгезивным по типу реализации, например так:

public interface IChallenge
public interface IChallengeView
{
    IChallenge Challenge {get;set;}
}
public interface IChallengeHostView
{
    IChallengeView ChallengeView { get; set; }
}

public class AdditionChallenge : IChallenge {}
public class AdditionChallengeView: IChallengeView {}

Это означает, что у меня есть еще немного кода приведения вAdditionChallengeView (и все другие реализующие классы), но мне кажется, что это единственный жизнеспособный способ в то время.

Ответы [ 2 ]

1 голос
/ 05 марта 2012

Если вы используете c # 4 (или выше), вы можете воспользоваться дисперсией. Попробуйте объявить ваш IChallengeView<T> интерфейс ковариантным следующим образом:

public interface IChallengeView<out T> where T : IChallenge {}
0 голосов
/ 08 ноября 2013

Часто бывает полезно выделить части интерфейса, которые используют параметр типа ковариантным образом, и те, которые используют один и тот же метод.Это часто требует использования метода "SetProperty", а не свойства чтения-записи (по любой причине, если интерфейс наследует интерфейс, который включает в себя свойство только для чтения Foo, и другой, который реализует свойство только для записи Foo, компилятор скажет, что любые попытки доступа к свойству являются «неоднозначными» и не разрешит чтение или запись foo, несмотря на тот факт, что доступ для чтения может ссылаться только на свойство только для чтения и доступ на запись всвойство только для записи. Тем не менее, разделение контравариантных и ковариантных аспектов интерфейса часто позволяет использовать дисперсию в тех случаях, когда это было бы полезно и имеет смысл. Кроме того, разделение частей интерфейса, которые читают объект, частов любом случае полезно.

Одно небольшое замечание: я бы предложил, чтобы при использовании интерфейса использовались следующие термины, чтобы иметь указанные значения:

  • "читаемый" fooинтерфейс должен обеспечивать средства для чтения чаracteristics объекта, но не должен давать никаких обещаний о том, что объект может быть доступен для записи с использованием других средств.

  • Интерфейс foo "только для чтения" должен не только обеспечивать средства длячтение характеристик объекта, но следует также пообещать, что можно предоставить ссылку на любую легитимную реализацию без предоставления средств записи объекту.Однако нет никаких обещаний, что не существует каких-то других средств, с помощью которых можно изменить объект.

  • «неизменный» интерфейс foo должен обещать, что любое свойство, которое наблюдаетсяиметь заданное значение всегда будет иметь это значение.

Если код должен просто считывать, что находится в объекте, он может запросить «IReadableFoo».Если код использует ссылку на объект для краткосрочных данных инкапсуляции, которые он хочет предоставить другому коду, но ему не разрешено предоставлять сам объект чему-либо, что может его изменить, он должен обернуть объект в оболочку только для чтения.если только он не может безопасно выставить объект напрямую (на что указывает объект, реализующий IReadOnlyFoo. Если код хочет сохранить ссылку как средство сохранения снимка данных в нем, он должен сделать копию объекта, если естьлюбая вероятность, что это может измениться, но не должно беспокоить, если объект всегда будет одинаковым (обозначено IImmutableFoo).

...