Объект C# возвращает внутренние компоненты ⇐ C#
-
Гость
Объект C# возвращает внутренние компоненты
Цель
Очень повторяющийся дизайн, который я хотел бы реализовать на C#, следующий: класс, который владеет несколькими экземплярами другого класса.
Для ясности возьмем пример, скажем, «автомобиль», имеющий четыре «колеса».
Вот ограничения, которые я хочу выполнить:
[*]
Я хочу, чтобы машина показывала свои колеса, а не скрывала их. => В классе автомобилей должно быть что-то вроде
public Wheel GetWheel(слева, сзади) [*]Я хочу, чтобы машина могла каким-то образом обновлять свои колеса. Например, в классе автомобиля может быть такой метод:
public void ChangeTires(TireType) [*]Я не хочу, чтобы кто-то еще мог обновлять колеса. Например, не должно быть возможности запустить что-то вроде:
aCar.GetWheel(Left, Rear).SetTire(someTireType); // Не компилируется Это очень повторяющаяся схема: у сети есть узлы, у стула есть спинка, у пользователя есть учетные данные, у сокета есть IP-адрес, у банковского счета есть владелец...
Я читал форумы и спрашивал коллег, как они это делают. Мне было дано несколько ответов. Вот они.
Мои вопросы:
[*]Есть ли другой способ достижения этой цели, не указанный выше? [*]Существует ли общее мнение о «хорошем» способе действий? [*]Есть ли где-нибудь документированное размышление на эту тему? Блестящие люди, которые создали C# или широко его используют, вероятно, давно решили эту тему.
Попытка решения 1: использование внутреннего
Некоторые советуют ограничить видимость метода SetTire текущей сборкой:
класс Колесо { внутренняя пустота setTire (TireType someTireType); } Только «файлы в той же сборке», что и Wheel, могут обновлять его (менять шины).
Но это подразумевает, что Car и Wheel определены в одной и той же сборке, что в силу транзитивности приводит к тому, что все определяется в одной и той же сборке.
Кроме того, недостаточно ограничиться одной сборкой, я хочу ограничиться Car.
Попытка решения 2: вернуть копию
Некоторые советуют, чтобы метод GetWheel возвращал копию:
класс автомобиля { общественное колесо LeftRearWheel; общедоступное колесо GetWheel(LeftOrRight, FrontOrRear) { вернуть LeftRearWheel.DeepCopy(); } } При таком решении клиентскому коду ничего не сообщает о том, что колесо является копией. Это означает, что каждый раз, когда клиентский программист записывает метод get, он не знает, может ли он обновить возвращаемый объект или нет.
Кроме того, глубокое копирование всего, что вы используете в приложении, может снизить производительность.
Иными словами: следующий код компилируется, но ничего не делает:
aCar.GetWheel(Left, Rear).SetTire(someTireType); // Компилируется, но ничего не делает Попытка решения 3: создать неизменяемое колесо
Некоторые советуют сделать это:
публичный класс ReadonlyWheel { общественная шина getTire(); } внутренний класс Wheel: ReadonlyWheel { внутренняя пустота setTire(Tire); } Автомобиль общественного класса { общественное колесо LeftRearWheel; public ReadonlyWheel GetWheel(LeftOrRight, FrontOrRear) { вернуть LeftRearWheel; } } Вариант — объявить интерфейс
публичный интерфейс IWheel { Тип шины getTire(); } внутренний класс Колесо: IWheel { общественный тип TireType getTire(); внутренняя пустота setTire(Tire); } Автомобиль общественного класса { общественное колесо LeftRearWheel; общедоступный IWheel (LeftOrRight, FrontOrRear) { вернуть LeftRearWheel; } } Это решение действительно достигает цели, но предполагает значительное дублирование кода и дополнительную сложность. Поскольку это очень повторяющийся шаблон, я бы хотел избежать дублирования кода.
Попытка решения 4: без разницы
Некоторые люди утверждают, что сам вопрос выходит за рамки «образа мышления C#».
По мнению некоторых, обеспечение того, чтобы мои пользователи не делали «глупых» вещей с моей моделью, не моя забота.
Вариант: «если это произойдет, значит, ваша модель плохая». Колесо должно быть либо полностью частным, либо полностью общедоступным.
Я не могу заставить себя так думать. Возможно, кто-нибудь мог бы указать на любую документацию, объясняющую, как «образ мышления C#» обеспечивает такого рода моделирование.
Попытка решения 5: использование ReadOnlyCollection
Если объект, который я хочу показать, является контейнером, существует класс с именем ReadOnlyCollection, который, насколько я понимаю, объединяет попытки решения 2 и 3: он создает копию и встраивает ее в интерфейс только для чтения:
класс автомобиля { частный список myWheels; общественный ReadOnlyCollection GetWheels() { вернуть myWheels.AsReadOnly(); } } Это частично достигает цели: предупреждает пользователя, что ему не следует пытаться обновить коллекцию. Но это не может предотвратить обновление. Другими словами, следующий код компилируется и ничего не делает.
aCar.GetWheels()[0].SetTire(someTireType); // Компилируем, но ничего не делаем Кроме того, это не распространяется на не-коллекции.
Цель
Очень повторяющийся дизайн, который я хотел бы реализовать на C#, следующий: класс, который владеет несколькими экземплярами другого класса.
Для ясности возьмем пример, скажем, «автомобиль», имеющий четыре «колеса».
Вот ограничения, которые я хочу выполнить:
[*]
Я хочу, чтобы машина показывала свои колеса, а не скрывала их. => В классе автомобилей должно быть что-то вроде
public Wheel GetWheel(слева, сзади) [*]Я хочу, чтобы машина могла каким-то образом обновлять свои колеса. Например, в классе автомобиля может быть такой метод:
public void ChangeTires(TireType) [*]Я не хочу, чтобы кто-то еще мог обновлять колеса. Например, не должно быть возможности запустить что-то вроде:
aCar.GetWheel(Left, Rear).SetTire(someTireType); // Не компилируется Это очень повторяющаяся схема: у сети есть узлы, у стула есть спинка, у пользователя есть учетные данные, у сокета есть IP-адрес, у банковского счета есть владелец...
Я читал форумы и спрашивал коллег, как они это делают. Мне было дано несколько ответов. Вот они.
Мои вопросы:
[*]Есть ли другой способ достижения этой цели, не указанный выше? [*]Существует ли общее мнение о «хорошем» способе действий? [*]Есть ли где-нибудь документированное размышление на эту тему? Блестящие люди, которые создали C# или широко его используют, вероятно, давно решили эту тему.
Попытка решения 1: использование внутреннего
Некоторые советуют ограничить видимость метода SetTire текущей сборкой:
класс Колесо { внутренняя пустота setTire (TireType someTireType); } Только «файлы в той же сборке», что и Wheel, могут обновлять его (менять шины).
Но это подразумевает, что Car и Wheel определены в одной и той же сборке, что в силу транзитивности приводит к тому, что все определяется в одной и той же сборке.
Кроме того, недостаточно ограничиться одной сборкой, я хочу ограничиться Car.
Попытка решения 2: вернуть копию
Некоторые советуют, чтобы метод GetWheel возвращал копию:
класс автомобиля { общественное колесо LeftRearWheel; общедоступное колесо GetWheel(LeftOrRight, FrontOrRear) { вернуть LeftRearWheel.DeepCopy(); } } При таком решении клиентскому коду ничего не сообщает о том, что колесо является копией. Это означает, что каждый раз, когда клиентский программист записывает метод get, он не знает, может ли он обновить возвращаемый объект или нет.
Кроме того, глубокое копирование всего, что вы используете в приложении, может снизить производительность.
Иными словами: следующий код компилируется, но ничего не делает:
aCar.GetWheel(Left, Rear).SetTire(someTireType); // Компилируется, но ничего не делает Попытка решения 3: создать неизменяемое колесо
Некоторые советуют сделать это:
публичный класс ReadonlyWheel { общественная шина getTire(); } внутренний класс Wheel: ReadonlyWheel { внутренняя пустота setTire(Tire); } Автомобиль общественного класса { общественное колесо LeftRearWheel; public ReadonlyWheel GetWheel(LeftOrRight, FrontOrRear) { вернуть LeftRearWheel; } } Вариант — объявить интерфейс
публичный интерфейс IWheel { Тип шины getTire(); } внутренний класс Колесо: IWheel { общественный тип TireType getTire(); внутренняя пустота setTire(Tire); } Автомобиль общественного класса { общественное колесо LeftRearWheel; общедоступный IWheel (LeftOrRight, FrontOrRear) { вернуть LeftRearWheel; } } Это решение действительно достигает цели, но предполагает значительное дублирование кода и дополнительную сложность. Поскольку это очень повторяющийся шаблон, я бы хотел избежать дублирования кода.
Попытка решения 4: без разницы
Некоторые люди утверждают, что сам вопрос выходит за рамки «образа мышления C#».
По мнению некоторых, обеспечение того, чтобы мои пользователи не делали «глупых» вещей с моей моделью, не моя забота.
Вариант: «если это произойдет, значит, ваша модель плохая». Колесо должно быть либо полностью частным, либо полностью общедоступным.
Я не могу заставить себя так думать. Возможно, кто-нибудь мог бы указать на любую документацию, объясняющую, как «образ мышления C#» обеспечивает такого рода моделирование.
Попытка решения 5: использование ReadOnlyCollection
Если объект, который я хочу показать, является контейнером, существует класс с именем ReadOnlyCollection, который, насколько я понимаю, объединяет попытки решения 2 и 3: он создает копию и встраивает ее в интерфейс только для чтения:
класс автомобиля { частный список myWheels; общественный ReadOnlyCollection GetWheels() { вернуть myWheels.AsReadOnly(); } } Это частично достигает цели: предупреждает пользователя, что ему не следует пытаться обновить коллекцию. Но это не может предотвратить обновление. Другими словами, следующий код компилируется и ничего не делает.
aCar.GetWheels()[0].SetTire(someTireType); // Компилируем, но ничего не делаем Кроме того, это не распространяется на не-коллекции.
Мобильная версия