В C ++ друг ключевое слово позволяет class A
назначить в class B
качестве своего друга. Это позволяет Class B
получить доступ к private
/ protected
членов class A
.
Я никогда не читал ничего о том, почему это осталось за пределами C # (и VB.NET). Большинство ответов на этот более ранний вопрос StackOverflow говорят о том, что это полезная часть C ++, и есть веские причины для ее использования. По своему опыту я бы согласился.
Мне кажется, что другой вопрос на самом деле спрашивает, как сделать что-то подобное friend
в приложении на C #. Хотя ответы, как правило, связаны с вложенными классами, это не так элегантно, как использование friend
ключевого слова.
Оригинальная книга «Design Patterns» регулярно использует ее в своих примерах.
Итак, в заключение, почему friend
отсутствует в C #, и каков «наилучший метод» (или способы) его моделирования в C #?
(К слову сказать, internal
ключевое слово не то же самое, что позволяет все классы в рамках всей сборки для доступа internal
членов, в то время как friend
позволяет дать определенный класс полный доступ к точно один другой класс)
Ответы:
Наличие друзей в программировании более или менее считается "грязным" и легко злоупотреблять. Это нарушает отношения между классами и подрывает некоторые фундаментальные атрибуты ОО-языка.
При этом это хорошая функция, и я сам много раз использовал ее в C ++; и хотел бы использовать его в C # тоже. Но я держу пари, что из-за «чистого» OOness в C # (по сравнению с псевдо-OOness в C ++) MS решила, что, поскольку у Java нет ключевого слова-друга, C # тоже не должен (просто шучу;))
Серьезное замечание: внутренний не так хорош, как друг, но он выполняет свою работу. Помните, что редко вы будете распространять свой код среди сторонних разработчиков не через DLL; так что, пока вы и ваша команда знаете о внутренних классах и их использовании, у вас должно быть все в порядке.
РЕДАКТИРОВАТЬ Позвольте мне уточнить, как ключевое слово друга подрывает ООП.
Частные и защищенные переменные и методы, возможно, являются одной из наиболее важных частей ООП. Идея о том, что объекты могут содержать данные или логику, которую могут использовать только они, позволяет вам писать реализацию вашей функциональности независимо от вашей среды - и что ваша среда не может изменять информацию о состоянии, которую она не подходит для обработки. Используя друга, вы соединяете реализации двух классов вместе - что гораздо хуже, чем если бы вы просто связали их интерфейс.
источник
friend
не позволяет произвольным классам или функциям получать доступ к закрытым членам. Это позволяет определенным функциям или классам, которые были отмечены как друзья, делать это. Класс, владеющий закрытым членом, все еще контролирует доступ к нему на 100%. С таким же успехом можно утверждать, что публичные методы-члены. Оба гарантируют, что методы, конкретно перечисленные в классе, получают доступ к закрытым членам класса.На боковой ноте. Использование друга заключается не в нарушении инкапсуляции, а в принудительном применении. Как аксессоры + мутаторы, перегрузка операторов, публичное наследование, даункастинг и т. Д. , Он часто используется неправильно, но это не означает, что ключевое слово не имеет или, что еще хуже, имеет плохую цель.
См Konrad Rudolph «s сообщение в другом потоке, или , если вы предпочитаете видеть соответствующую запись в C ++ FAQ.
источник
friend
дает ему доступ ко ВСЕМ вашим личным членам, даже если вам нужно только разрешить ему установить одного из них. Существуют веские аргументы в пользу того, что предоставление полей другим классам, которым они не нужны, считается «нарушением инкапсуляции». В C ++ есть места, где это необходимо (операторы перегрузки), но есть компромисс между инкапсуляцией, который необходимо тщательно рассмотреть. Кажется, дизайнеры C # чувствовали, что компромисс не стоит того.Для информации, еще одна связанная, но не совсем та же вещь в .NET
[InternalsVisibleTo]
, которая позволяет сборке назначать другую сборку (например, сборку модульного теста), которая (эффективно) имеет «внутренний» доступ к типам / элементам в оригинальная сборка.источник
Вы должны быть в состоянии выполнить те же самые вещи, для которых «друг» используется в C ++, используя интерфейсы в C #. Это требует от вас явного определения, какие члены передаются между двумя классами, что является дополнительной работой, но также может облегчить понимание кода.
Если у кого-то есть пример разумного использования «друга», которое нельзя смоделировать с помощью интерфейсов, поделитесь им! Я хотел бы лучше понять различия между C ++ и C #.
источник
С
friend
C ++ разработчик имеет точный контроль над тем, кому доступны частные * члены. Но он вынужден разоблачить каждого из частных участников.С
internal
C # дизайнер имеет точный контроль над набором закрытых членов, которые он выставляет. Очевидно, он может выставить только одного частного члена. Но он будет открыт для всех классов в сборке.Как правило, разработчик желает предоставить только несколько закрытых методов выбранным нескольким другим классам. Например, в шаблоне фабрики классов может быть желательно, чтобы класс C1 создавался только фабрикой классов CF1. Поэтому класс C1 может иметь защищенный конструктор и фабрику класса друга CF1.
Как видите, у нас есть 2 измерения, вдоль которых можно нарушить инкапсуляцию.
friend
нарушает это по одному измерению,internal
делает это по другому. Какой из них является худшим нарушением в концепции инкапсуляции? Тяжело сказать. Но было бы неплохо иметь и тоfriend
и другоеinternal
. Кроме того, хорошим дополнением к этим двум будет 3-й тип ключевого слова, которое будет использоваться по принципу «член за членом» (напримерinternal
) и определяет целевой класс (напримерfriend
).* Для краткости я буду использовать «частный» вместо «частный и / или защищенный».
Ник
источник
На самом деле, C # дает возможность получить то же поведение в чистом виде ООП без особых слов - это частные интерфейсы.
Что касается вопроса Что такое эквивалент C # друга?была помечена как дубликат этой статьи, и никто там не предложил действительно хорошую реализацию - я покажу ответ на оба вопроса здесь.
Основная идея была взята отсюда: что такое частный интерфейс?
Допустим, нам нужен некоторый класс, который мог бы управлять экземплярами других классов и вызывать для них некоторые специальные методы. Мы не хотим давать возможность вызывать эти методы другим классам. Это то же самое, что и ключевое слово Friend c ++ в мире c ++.
Я думаю, что хорошим примером в реальной практике может служить шаблон Full State Machine, в котором один контроллер обновляет текущий объект состояния и переключается на другой объект состояния, когда это необходимо.
Ты мог:
Controller.cs
Program.cs
Ну, а как насчет наследства?
Нам нужно использовать технику, описанную в разделе Поскольку явные реализации членов интерфейса не могут быть объявлены виртуальными и помечать IState как защищенные, чтобы дать возможность также наследовать от Controller.
Controller.cs
PlayerIdleState.cs
И, наконец, пример того, как проверить наследование класса Controller: ControllerTest.cs
Надеюсь, я расскажу обо всех случаях, и мой ответ был полезен.
источник
Друг чрезвычайно полезен при написании юнит-теста.
Несмотря на то, что это приводит к незначительному загрязнению объявления вашего класса, оно также является напоминанием с помощью компилятора о том, какие тесты действительно могут заботиться о внутреннем состоянии класса.
Очень полезная и понятная идиома, которую я нашел, - это когда у меня есть фабричные классы, они подружились с созданными ими предметами, которые имеют защищенный конструктор. Точнее говоря, это было, когда у меня была одна фабрика, отвечающая за создание соответствующих объектов рендеринга для объектов автора отчетов, рендеринга в заданную среду. В этом случае вы обладаете единой информацией о взаимосвязи между классами автора отчетов (такими как блоки изображений, полосы макета, заголовки страниц и т. Д.) И соответствующими им объектами рендеринга.
источник
В C # отсутствует ключевое слово "friend" по той же причине, что и в нем отсутствует детерминированное уничтожение. Изменение соглашений заставляет людей чувствовать себя умными, как будто их новые способы превосходят чьи-то старые способы. Это все о гордости.
Сказать, что «классы друзей плохие», так же недальновидно, как и другие неквалифицированные высказывания, такие как «не использовать gotos» или «Linux лучше Windows».
Ключевое слово "друг" в сочетании с прокси-классом - отличный способ показать только определенные части класса конкретному другому классу (-ам). Прокси-класс может действовать как надежный барьер против всех других классов. «public» не допускает подобного таргетирования, и использование «protected» для получения эффекта с наследованием неудобно, если на самом деле нет концептуальных отношений «is».
источник
Вы можете приблизиться к C ++ "другу" с ключевым словом C # "внутренним" .
источник
internal
приобретают гораздо больше смысла и обеспечивают отличный компромисс между инкапсуляцией и полезностью. Доступ по умолчанию в Java еще лучше.Это на самом деле не проблема с C #. Это фундаментальное ограничение в IL. C # ограничен этим, как и любой другой язык .Net, который стремится быть проверяемым. Это ограничение также включает управляемые классы, определенные в C ++ / CLI ( раздел 20.5 спецификации ).
При этом я думаю, что у Нельсона есть хорошее объяснение того, почему это плохо.
источник
friend
не плохая вещь; напротив, это намного лучше, чемinternal
. И объяснение Нельсона плохое и неправильное.Прекратите оправдываться за это ограничение. друг это плохо, а внутренний это хорошо? это одно и то же, только тот друг дает вам более точный контроль над тем, кому разрешен доступ, а кому нет.
Это для обеспечения парадигмы инкапсуляции? так что вы должны написать методы доступа и что теперь? как вы должны остановить всех (кроме методов класса B) от вызова этих методов? вы не можете, потому что вы не можете контролировать это либо из-за отсутствия "друга".
Ни один язык программирования не идеален. C # - один из лучших языков, которые я видел, но глупые оправдания отсутствующих функций никому не помогают. В C ++ я скучаю по простой системе событий / делегатов, рефлексии (+ автоматическая де / сериализация) и foreach, но в C # я скучаю по перегрузке операторов (да, повторю, что она вам не нужна), параметрам по умолчанию, const это не может быть обойдено, множественное наследование (да, продолжайте говорить мне, что вам это не нужно, и интерфейсы были достаточной заменой) и возможность решить удалить экземпляр из памяти (нет, это не ужасно плохо, если вы не ремесленник)
источник
||
/&&
удерживая их в коротком замыкании. Параметры по умолчанию были добавлены в C # 4.Начиная с .Net 3 существует атрибут InternalsVisibleToAttribute, но я подозреваю, что они добавили его только для обслуживания сборок после повышения модульного тестирования. Я не вижу много других причин, чтобы использовать его.
Он работает на уровне сборки, но выполняет работу там, где внутренняя - нет; то есть, где вы хотите распространить сборку, но хотите, чтобы другая нераспределенная сборка имела привилегированный доступ к ней.
Совершенно справедливо, что они требуют, чтобы сборка друзей была жесткой, чтобы кто-то не создавал притворного друга рядом с вашей защищенной сборкой.
источник
Я прочитал много умных комментариев о ключевом слове «друг», и я согласен, что это полезная вещь, но я думаю, что «внутреннее» ключевое слово менее полезно, и оба они все еще плохи для чистого ОО программирования.
Что мы имеем? (говоря о "друге", я также говорю о "внутреннем")
да;
не использует "друг" делает код лучше?
Использование друга создает некоторые локальные проблемы, а не использование создает проблемы для пользователей библиотеки кода.
общее хорошее решение для языка программирования я вижу так:
Что вы думаете об этом? Я думаю, что это наиболее распространенное и чистое объектно-ориентированное решение. Вы можете открыть доступ к любому выбранному вами методу для любого класса.
источник
Я отвечу только на вопрос «Как».
Здесь так много ответов, однако я хотел бы предложить своего рода «шаблон проектирования» для достижения этой функции. Я буду использовать простой языковой механизм, который включает в себя:
Например, у нас есть 2 основных класса: студент и университет. Студент имеет средний балл, к которому имеет доступ только университет. Вот код:
источник
Я подозреваю, что это как-то связано с моделью компиляции C # - сборкой IL JIT, компилирующей это во время выполнения. то есть: по той же причине, что дженерики C # принципиально отличаются от дженериков C ++.
источник
вы можете держать его в секрете и использовать рефлексию для вызова функций. Тестовый фреймворк может сделать это, если вы попросите его протестировать приватную функцию
источник
Раньше я регулярно использовал друга, и я не думаю, что это нарушение ООП или признак какого-либо недостатка дизайна. Есть несколько мест, где он является наиболее эффективным средством для достижения наименьшего количества кода.
Один конкретный пример - при создании сборок интерфейса, которые обеспечивают интерфейс связи с некоторым другим программным обеспечением. Обычно существует несколько тяжеловесных классов, которые обрабатывают сложность протокола и особенности одноранговых узлов и обеспечивают относительно простую модель подключения / чтения / записи / пересылки / отключения, включающую передачу сообщений и уведомлений между клиентским приложением и сборкой. Эти сообщения / уведомления должны быть заключены в классы. Атрибуты обычно должны управляться программным обеспечением протокола, поскольку оно является их создателем, но многие вещи должны оставаться доступными только для чтения для внешнего мира.
Просто глупо заявлять, что нарушением ООП является то, что у класса протокола / «создателя» есть интимный доступ ко всем созданным классам - классу-создателю приходилось разбирать каждый бит данных по пути вверх. Что я нашел наиболее важным, так это минимизировать все дополнительные строки кода BS, к которым обычно приводит модель «ООП ради ООП». Дополнительные спагетти только делают больше ошибок.
Знают ли люди, что вы можете применять ключевое слово internal на уровне атрибута, свойства и метода? Это не только для объявления класса верхнего уровня (хотя большинство примеров, кажется, показывают это.)
Если у вас есть класс C ++, использующий ключевое слово friend, и вы хотите эмулировать его в классе C #: 1. объявите открытый класс C # 2. объявите все атрибуты / свойства / методы, которые защищены в C ++ и, таким образом, доступны для друзей как внутренние в C # 3. создать свойства только для чтения для общего доступа ко всем внутренним атрибутам и свойствам
Я согласен, что это не на 100% то же самое, что и друг, и модульное тестирование является очень ценным примером необходимости чего-то вроде друга (как, например, код журнала анализатора протокола). Однако внутренняя обеспечивает доступ к классам, которые вы хотите получить, а [InternalVisibleTo ()] обрабатывает все остальное - похоже, он был создан специально для модульного тестирования.
Что касается друга, который «лучше, потому что вы можете точно контролировать, какие классы имеют доступ» - что, черт возьми, куча подозрительных злых классов делает в одной сборке? Разбейте свои сборки!
источник
Дружба может быть смоделирована путем разделения интерфейсов и реализаций. Идея такова: « Требовать конкретного экземпляра, но ограничить доступ к конструкции этого экземпляра ».
Например
Несмотря на то, что
ItsMeYourFriend()
public является общедоступным, толькоFriend
класс может получить к нему доступ, поскольку никто другой не может получить конкретный экземплярFriend
класса. У него есть приватный конструктор, а фабричныйNew()
метод возвращает интерфейс.См. Мою статью « Друзья и члены внутреннего интерфейса бесплатно» с кодированием интерфейсов для деталей.
источник
Некоторые предполагают, что вещи могут выйти из-под контроля с помощью друга. Я бы согласился, но это не умаляет его полезности. Я не уверен, что друг обязательно повредит парадигме ОО больше, чем обнародует всех ваших учеников. Конечно, язык позволит вам сделать всех ваших участников публичными, но это дисциплинированный программист, который избегает такого типа шаблона проектирования. Точно так же дисциплинированный программист зарезервировал бы использование друга для определенных случаев, где это имеет смысл. Я чувствую, что внутреннее разоблачение слишком много в некоторых случаях. Зачем выставлять класс или метод всему в сборке?
У меня есть страница ASP.NET, которая наследует мою собственную базовую страницу, которая, в свою очередь, наследует System.Web.UI.Page. На этой странице у меня есть некоторый код, который обрабатывает отчеты об ошибках конечного пользователя для приложения в защищенном методе
Теперь у меня есть пользовательский элемент управления, который содержится на странице. Я хочу, чтобы пользовательский элемент управления мог вызывать методы сообщения об ошибках на странице.
Это не может быть сделано, если метод ReportError защищен. Я могу сделать его внутренним, но он подвергается воздействию любого кода в сборке. Я просто хочу, чтобы он был открыт для элементов пользовательского интерфейса, которые являются частью текущей страницы (включая дочерние элементы управления). В частности, я хочу, чтобы мой базовый класс управления определял те же методы сообщения об ошибках и просто вызывал методы на базовой странице.
Я считаю, что что-то вроде друга может быть полезным и реализовано в языке, не делая язык менее "ОО", как, например, как атрибуты, так что вы можете иметь классы или методы, дружить с конкретными классами или методами, что позволяет разработчику конкретный доступ. Возможно, что-то вроде ... (псевдокод)
В моем предыдущем примере, возможно, есть что-то вроде следующего (можно поспорить с семантикой, но я просто пытаюсь донести идею):
На мой взгляд, концепция «друг» несет в себе не больший риск, чем обнародование информации или создание открытых методов или свойств для доступа к членам. Во всяком случае, друг допускает другой уровень детализации в доступности данных и позволяет вам сузить эту доступность, а не расширять ее внутренними или общедоступными.
источник
Если вы работаете с C ++ и обнаруживаете, что используете ключевое слово friend самостоятельно, это очень убедительный признак того, что у вас есть проблема с дизайном, потому что, черт возьми, классу нужен доступ к закрытым членам другого класса ??
источник
Bsd
Было заявлено, что друзья болят чистым OOness. С чем я согласен.
Также было заявлено, что друзья помогают инкапсуляции, с чем я тоже согласен.
Я думаю, что дружба должна быть добавлена к методологии ОО, но не совсем так, как в С ++. Я хотел бы иметь некоторые поля / методы, к которым может обращаться мой класс друзей, но я бы не хотел, чтобы они имели доступ ко ВСЕМ моим полям / методам. Как и в реальной жизни, я бы позволил своим друзьям получить доступ к моему личному холодильнику, но я бы не позволил им получить доступ к моему банковскому счету.
Можно реализовать это следующим образом
Это, конечно, сгенерирует предупреждение компилятора и повредит intellisense. Но это сделает работу.
С другой стороны, я думаю, что уверенный программист должен выполнять тестирование без доступа к закрытым членам. это совершенно выходит за рамки, но попробуйте прочитать о TDD. однако, если вы все еще хотите это сделать (имея c ++, как друзья), попробуйте что-то вроде
поэтому вы пишете весь свой код без определения UNIT_TESTING, а когда вы хотите выполнить модульное тестирование, вы добавляете #define UNIT_TESTING в первую строку файла (и записываете весь код, выполняющий модульное тестирование, в #if UNIT_TESTING). С этим нужно обращаться осторожно.
Поскольку я считаю, что юнит-тестирование - плохой пример использования друзей, я бы привел пример, почему я считаю, что друзья могут быть хорошими. Предположим, у вас есть ломающая система (класс). При использовании система разрушения изнашивается и нуждается в ремонте. Теперь вы хотите, чтобы это сделал только лицензированный механик. Чтобы сделать пример менее тривиальным, я бы сказал, что механик будет использовать его личную (личную) отвертку, чтобы исправить это. Вот почему класс механики должен быть другом класса BreakSystem.
источник
c.MyMethod((FriendClass) null, 5.5, 3)
из любого класса.Дружба также может быть смоделирована с помощью «агентов» - некоторых внутренних классов. Рассмотрим следующий пример:
Это может быть намного проще, когда используется для доступа только к статическим элементам. Преимущества такой реализации состоят в том, что все типы объявлены во внутренней области классов дружбы и, в отличие от интерфейсов, обеспечивают доступ к статическим членам.
источник