Почему C# не предоставляет ключевое слово 'friend' стиля C++? [закрытый]


The в C++ friend ключевое слово позволяет a class A назначить class B как его друг. Это позволяет Class B к private/protected члены class A.

я никогда не читал ничего о том, почему это было оставлено вне C# (и VB.NET). большинство ответов на это ранее StackOverflow вопрос кажется, говорят, что это полезная часть C++ и есть веские причины, чтобы использовать его. По своему опыту я должен был бы согласиться.

другой вопрос мне кажется, что я действительно спрашиваю, как сделать что-то подобное friend в приложении C#. Хотя ответы обычно вращаются вокруг вложенных классов, это не кажется таким элегантным, как использование friend ключевое слово.

оригинал Design Patterns book использует его регулярно во всех своих примерах.

Итак, вкратце, почему это friend отсутствует в C#, и что такое" лучшая практика " способ (или способы) его моделирования в C#?

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

23 192

23 ответа:

наличие друзей в программировании более или менее считается "грязным" и легко злоупотреблять. Это нарушает отношения между классами и подрывает некоторые фундаментальные атрибуты языка OO.

Это, как говорится, хорошая функция, и я сам использовал ее много раз в C++; и хотел бы использовать ее в C# тоже. Но я уверен, что из-за "чистой" OOness C#(по сравнению с псевдо-OOness C++) MS решила, что, поскольку Java не имеет ключевого слова друга, C# тоже не должен (просто шучу ;))

на серьезной ноте: внутренний не так хорош, как друг, но он выполняет свою работу. Помните, что вы редко будете распространять свой код сторонним разработчикам не через DLL; поэтому, пока вы и ваша команда знаете о внутренних классах и их использовании, вы должны быть в порядке.

EDIT позвольте мне уточнить, как ключевое слово friend подрывает ООП.

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

на боковой ноте. Использование friend-это не нарушение инкапсуляции,а наоборот, ее применение. Как аксессоры+мутаторы, операторы перегрузки, публичное наследование, downcasting,etc., Он часто используется неправильно, но это не значит, что ключевое слово не имеет или, что еще хуже, плохой цели.

посмотреть Конрад Рудольф ' s в другом потоке, или если вы предпочитаете видеть соответствующую запись в C++ FAQ.

для информации, другое связанное, но не совсем то же самое в .NET-это [InternalsVisibleTo], что позволяет сборке назначать другую сборку (например, сборку модульного теста), которая (фактически) имеет "внутренний" доступ к типам/элементам в исходной сборке.

вы должны быть в состоянии выполнить те же самые вещи, что "друг" используется в C++ с помощью интерфейсов в C#. Это требует, чтобы вы явно определили, какие члены передаются между двумя классами, что является дополнительной работой, но также может облегчить понимание кода.

Если у кого-то есть пример разумного использования "друга", который не может быть смоделирован с помощью интерфейсов, пожалуйста, поделитесь им! Я хотел бы лучше понять различия между C++ и С.#

С friend конструктор C++ имеет точный контроль над тем, кому подвергаются члены private*. Но, он вынужден разоблачить каждого из частных членов.

С internal дизайнер C# имеет точный контроль над набором частных членов, которые он предоставляет. Очевидно, что он может разоблачить только одного частного члена. Но, он будет открыт для всех классов в сборке.

как правило, дизайнер хочет предоставить только несколько частных методов выбранным немногим другое занятие. Например, в шаблоне фабрики классов может быть желательно, чтобы экземпляр класса C1 создавался только фабрикой классов CF1. Поэтому класс C1 может иметь защищенный конструктор и дружественный класс factory CF1.

как вы можете видеть, у нас есть 2 измерения, вдоль которых инкапсуляция может быть нарушена. friend нарушения его вдоль одного измерения, internal она вместе с другой. Какой из них является худшим нарушением концепции инкапсуляции? Трудно сказать. Но было бы неплохо иметь и то и другое friend и internal доступен. Кроме того, хорошим дополнением к этим двум будет ключевое слово 3-го типа, которое будет использоваться на основе члена за членом (например,internal) и указывает целевой класс (например friend).

* для краткости я буду использовать "частный "вместо"частный и/или защищенный".

- Ник

вы можете приблизиться к C++ "другу" с ключевым словом C#"внутренних".

друг чрезвычайно полезен при написании модульного теста.

хотя это происходит за счет незначительного загрязнения вашего объявления класса, это также принудительное напоминание компилятора о том, какие тесты на самом деле могут заботиться о внутреннем состоянии класса.

очень полезная и чистая идиома, которую я нашел, - это когда у меня есть заводские классы, что делает их друзьями элементов, которые они создают, которые имеют защищенный конструктор. Более конкретно, это было, когда у меня была одна фабрика отвечает за создание соответствующих объектов рендеринга для объектов записи отчетов, рендеринга в заданную среду. В этом случае у вас есть одна точка знания о связи между классами report-writer (такие вещи, как блоки изображений, полосы макета, заголовки страниц и т. д.) и их соответствующие объекты рендеринга.

Это на самом деле не проблема с C#. Это фундаментальное ограничение в IL. C# ограничен этим, как и любой другой язык .Net, который стремится быть проверяемым. Это ограничение также включает управляемые классы, определенные в C++ / CLI ( раздел спецификации 20.5).

иными словами, Я думаю, что у Нельсона есть хорошее объяснение, почему это плохо.

В C# отсутствует ключевое слово "friend" по той же причине, по которой отсутствует детерминированное разрушение. Изменение условностей заставляет людей чувствовать себя умными, как будто их новые пути превосходят чьи-то старые пути. Все дело в гордости.

говоря, что" классы друзей плохие "так же близоруки, как и другие неквалифицированные заявления, такие как" не используйте gotos "или"Linux лучше, чем Windows".

ключевое слово "друг" в сочетании с прокси-классом-отличный способ только выставить некоторых запасные части класса к определенным другим классом(классами). Прокси-класс может выступать в качестве надежного барьера против всех других классов. "public "не допускает такого таргетинга, и использование" protected "для получения эффекта с наследованием неудобно, если действительно нет концептуальных отношений" is a".

фактически, C# дает возможность получить такое же поведение в чистом виде ООП без специальных слов-это частные интерфейсы.

Что касается вопроса Что такое c# эквивалент друга? был отмечен как дубликат этой статьи, и никто там не предлагает действительно хорошую реализацию - я покажу ответ на оба вопроса здесь.

основная идея была взята отсюда:что такое частный интерфейс?

скажем, нам нужен какой-то класс, который может управлять экземплярами других классов и вызывать некоторые специальные методы на них. Мы не хотим давать возможность вызывать эти методы каким-либо другим классам. Это точно то же самое, что ключевое слово friend c++ делает в мире c++.

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

вы можете:

  • самый простой и худший способ сделать метод Update() общедоступным-Надежда все понимают, почему это плохо.
  • Следующий способ-отметить его как внутренний. Это достаточно хорошо, если вы положите свой классы для другой сборки, но даже тогда каждый класс в этой сборке можно вызвать каждый внутренний метод.
  • использовать закрытый / защищенный интерфейс-и я пошел этим путем.

.cs

public class Controller
{
    private interface IState
    {
        void Update();
    }

    public class StateBase : IState
    {
        void IState.Update() {  }
    }

    public Controller()
    {
        //it's only way call Update is to cast obj to IState
        IState obj = new StateBase();
        obj.Update();
    }
}

.cs

public class Controller
{
    protected interface IState
    {
        void Update();
    }

    public class StateBase : IState
    {
        void IState.Update() { OnUpdate(); }
        protected virtual void OnUpdate()
        {
            Console.WriteLine("StateBase.OnUpdate()");
        }
    }

    public Controller()
    {
        IState obj = new PlayerIdleState();
        obj.Update();
    }
}

PlayerIdleState.cs

public class PlayerIdleState: Controller.StateBase
{
    protected override void OnUpdate()
    {
        base.OnUpdate();
        Console.WriteLine("PlayerIdleState.OnUpdate()");
    }
}

и, наконец, пример, как проверить класс контроллера броска наследование: ControllerTest.cs

class ControllerTest: Controller
{
    public ControllerTest()
    {
        IState testObj = new PlayerIdleState();
        testObj.Update();
    }
}

надеюсь, что охватить все случаи, и мой ответ был полезен.

прекратите оправдываться за это ограничение. друг-это плохо, но внутренний-это хорошо? это одно и то же, только этот друг дает вам более точный контроль над тем, кто имеет доступ, а кто нет.

Это для обеспечения соблюдения парадигмы инкапсуляции? Итак, вам нужно написать методы доступа, и что теперь? как вы должны остановить всех (кроме методов класса B) от вызова этих методов? вы не можете, потому что вы не можете контролировать это, из-за отсутствия "друг."

ни один язык программирования не является совершенным. C# - один из лучших языков, которые я видел, но глупые оправдания отсутствующих функций никому не помогают. В C++ я пропускаю легкую систему событий / делегатов, отражение (+автоматическая де / сериализация) и foreach, но в C# я пропускаю перегрузку оператора (да, продолжайте говорить мне, что вам это не нужно), параметры по умолчанию, константу, которую нельзя обойти, множественное наследование (да, продолжайте говорить мне, что вам это не нужно и интерфейсы были достаточной заменой) и возможность решить, чтобы удалить экземпляр из памяти (нет, это не ужасно плохо, если вы не мастер)

существует InternalsVisibleToAttribute с .Net 3, но я подозреваю, что они добавили его только для обслуживания тестовых сборок после подъема модульного тестирования. Я не вижу других причин, чтобы использовать его.

Он работает на уровне сборки, но выполняет работу там, где внутренний не работает; то есть там, где вы хотите распространять сборку, но хотите, чтобы другая нераспределенная сборка имела привилегированный доступ к ней.

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

Я прочитал много умных комментариев о ключевом слове "друг", и я согласен, что это полезно, но я думаю, что" внутреннее " ключевое слово менее полезно, и они оба по-прежнему плохо подходят для чистого программирования OO.

что мы имеем? (говоря о "друге" я также говорю о "внутреннем")

  • использование "друга" делает код менее чистым относительно oo?
  • да,

  • Не использование "друга" делает код лучше?

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

использование друга создает некоторые локальные проблемы, а не использование его создает проблемы для пользователей библиотеки кода.

общее хорошее решение для языка программирования я вижу так:

// c++ style
class Foo {
  public_for Bar:
    void addBar(Bar *bar) { }
  public:
  private:
  protected:
};

// c#
class Foo {
    public_for Bar void addBar(Bar bar) { }
}

что вы об этом думаете? Я думаю это наиболее распространенное и чистое объектно-ориентированное решение. Вы можете открыть доступ к любому выбранному вами методу к любому классу, который вы хотите.

Я подозреваю, что это как-то связано с моделью компиляции C# - создание IL JIT-компиляции во время выполнения. т. е.: по той же причине, что c# дженерики принципиально отличаются от c++ дженериков.

Если вы работаете с C++, и вы находите себя с помощью ключевого слова friend, это очень сильный признак того, что у вас есть проблема с дизайном, потому что, черт возьми, класс должен получить доступ к закрытым членам другого класса??

вы можете сохранить его частным и использовать отражение для вызова функций. Test framework может сделать это, если вы попросите его протестировать частную функцию

Я регулярно использовал друга, и я не думаю, что это какое-либо нарушение ООП или признак какого-либо дефекта дизайна. Есть несколько мест, где это наиболее эффективное средство для правильной цели с наименьшим количеством кода.

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

просто глупо заявлять, что это нарушение ООП для класса protocol / " creator интимный доступ ко всем созданным классам-класс создателя должен был бит munge каждый бит данных на пути вверх. То, что я нашел наиболее важным, - это свести к минимуму все дополнительные строки кода BS, к которым обычно приводит модель "ООП для ООП". Дополнительные спагетти просто делают больше ошибок.

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

Если у вас есть класс C++, который использует ключевое слово friend, и вы хотите эмулировать его в классе C# : 1. объявить класс C# public 2. объявите все атрибуты/свойства/методы, которые защищены в C++ и, следовательно, доступны для друзей как внутренние В C# 3. создайте свойства только для чтения для общего доступа ко всем внутренним атрибутам и свойствам

Я согласен, что это не 100% то же самое, что и друг, и модульный тест является очень ценным примером необходимости чего-то вроде друг (как и код регистрации анализатора протокола). Однако внутренний обеспечивает воздействие на классы, которые вы хотите иметь, и [InternalVisibleTo ()] обрабатывает остальное-похоже, что он был рожден специально для модульного теста.

Что касается друга "быть лучше, потому что вы можете явно контролировать, какие классы имеют доступ" - Что, черт возьми, куча подозрительных злых классов делает в той же сборке в первую очередь? Разделите ваши сборки!

дружба может быть смоделирована путем разделения интерфейсов и реализаций. Идея такова:"требуется конкретный экземпляр, но ограничить доступ к конструкции этого экземпляра".

interface IFriend { }

class Friend : IFriend
{
    public static IFriend New() { return new Friend(); }
    private Friend() { }

    private void CallTheBody() 
    {  
        var body = new Body();
        body.ItsMeYourFriend(this);
    }
}

class Body
{ 
    public void ItsMeYourFriend(Friend onlyAccess) { }
}

несмотря на то, что ItsMeYourFriend() только Friend класс может получить к нему доступ, так как никто другой не может получить конкретный экземпляр Friend класса. Он имеет частный конструктор, в то время как фабрика New() метод возвращает взаимодействие.

посмотреть мои статьи друзья и члены внутреннего интерфейса бесплатно с кодированием на интерфейсы для сведения.

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

у меня есть ASP.NET страница, которая наследует мою собственную базовую страницу, которая в свою очередь наследует систему.Сеть.ПОЛЬЗОВАТЕЛЬСКИЙ ИНТЕРФЕЙС.Страница. На этой странице у меня есть код, который обрабатывает отчеты об ошибках конечного пользователя для приложения в защищенном методе

ReportError("Uh Oh!");

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

MyBasePage bp = Page as MyBasePage;
bp.ReportError("Uh Oh");

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

protected void ReportError(string str) {
    MyBasePage bp = Page as MyBasePage;
    bp.ReportError(str);
}

Я считаю, что что-то как друг может быть полезно и реализовано на языке, не делая язык менее "OO", например, как атрибуты, так что вы можете иметь классы или методы быть друзьями для конкретных классов или методов, что позволяет разработчику предоставлять очень конкретный доступ. Возможно, что-то вроде этого...(псевдокод)

[Friend(B)]
class A {

    AMethod() { }

    [Friend(C)]
    ACMethod() { }
}

class B {
    BMethod() { A.AMethod() }
}

class C {
    CMethod() { A.ACMethod() }
}

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

class BasePage {

    [Friend(BaseControl.ReportError(string)]
    protected void ReportError(string str) { }
}

class BaseControl {
    protected void ReportError(string str) {
        MyBasePage bp = Page as MyBasePage;
        bp.ReportError(str);
    }
}

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

B. s. d.

было заявлено, что, друзья больно чистой OOness. И я с этим согласен.

Я думаю, что дружба должна быть добавлена к методологии OO, но не совсем так, как в C++. Я хотел бы иметь некоторые поля/методы, к которым может получить доступ мой класс friend, но я не хотел бы, чтобы они получили доступ ко всем моим полям/методам. Как и в реальной жизни, я бы позволил своим друзьям получить доступ к моему личному холодильнику, но я бы не позволил чтобы получить доступ к моему банковскому счету.

можно реализовать это следующим образом

    class C1
    {
        private void MyMethod(double x, int i)
        {
            // some code
        }
        // the friend class would be able to call myMethod
        public void MyMethod(FriendClass F, double x, int i)
        {
            this.MyMethod(x, i);
        }
        //my friend class wouldn't have access to this method 
        private void MyVeryPrivateMethod(string s)
        {
            // some code
        }
    }
    class FriendClass
    {
        public void SomeMethod()
        {
            C1 c = new C1();
            c.MyMethod(this, 5.5, 3);
        }
    }

Это, конечно, создаст предупреждение компилятора и повредит intellisense. Но это сделает свою работу.

на стороне примечание, Я думаю, что уверенный программист должен сделать блок тестирования без доступа к частным членам. это вполне выходит за рамки, но попробуйте прочитать о TDD. однако, если вы все еще хотите сделать это (имея c++, как друзья) попробуйте что-то как

#if UNIT_TESTING
        public
#else
        private
#endif
            double x;

дружба также может быть смоделирована с помощью "агентов" - некоторых внутренних классов. Рассмотрим следующий пример:

public class A // Class that contains private members
{
  private class Accessor : B.BAgent // Implement accessor part of agent.
  {
    private A instance; // A instance for access to non-static members.
    static Accessor() 
    { // Init static accessors.
      B.BAgent.ABuilder = Builder;
      B.BAgent.PrivateStaticAccessor = StaticAccessor;
    }
    // Init non-static accessors.
    internal override void PrivateMethodAccessor() { instance.SomePrivateMethod(); }
    // Agent constructor for non-static members.
    internal Accessor(A instance) { this.instance = instance; }
    private static A Builder() { return new A(); }
    private static void StaticAccessor() { A.PrivateStatic(); }
  }
  public A(B friend) { B.Friendship(new A.Accessor(this)); }
  private A() { } // Private constructor that should be accessed only from B.
  private void SomePrivateMethod() { } // Private method that should be accessible from B.
  private static void PrivateStatic() { } // ... and static private method.
}
public class B
{
  // Agent for accessing A.
  internal abstract class BAgent
  {
    internal static Func<A> ABuilder; // Static members should be accessed only by delegates.
    internal static Action PrivateStaticAccessor;
    internal abstract void PrivateMethodAccessor(); // Non-static members may be accessed by delegates or by overrideable members.
  }
  internal static void Friendship(BAgent agent)
  {
    var a = BAgent.ABuilder(); // Access private constructor.
    BAgent.PrivateStaticAccessor(); // Access private static method.
    agent.PrivateMethodAccessor(); // Access private non-static member.
  }
}

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

Я отвечу только на вопрос" как".

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

  • интерфейсы
  • вложенный класс

например, у нас есть 2 основных класса: студент и Университет. У студента есть средний балл, который разрешен только университету. Вот код:

public interface IStudentFriend
{
    Student Stu { get; set; }
    double GetGPS();
}

public class Student
{
    // this is private member that I expose to friend only
    double GPS { get; set; }
    public string Name { get; set; }

    PrivateData privateData;

    public Student(string name, double gps) => (GPS, Name, privateData) = (gps, name, new PrivateData(this);

    // No one can instantiate this class, but Student
    // Calling it is possible via the IStudentFriend interface
    class PrivateData : IStudentFriend
    {
        public Student Stu { get; set; }

        public PrivateData(Student stu) => Stu = stu;
        public double GetGPS() => Stu.GPS;
    }

    // This is how I "mark" who is Students "friend"
    public void RegisterFriend(University friend) => friend.Register(privateData);
}

public class University
{
    var studentsFriends = new List<IStudentFriend>();

    public void Register(IStudentFriend friendMethod) => studentsFriends.Add(friendMethod);

    public void PrintAllStudentsGPS()
    {
        foreach (var stu in studentsFriends)
            Console.WriteLine($"{stu.Stu.Name}: stu.GetGPS()");
    }
}

public static void Main(string[] args)
{
    var Technion = new University();
    var Alex     = new Student("Alex", 98);
    var Jo       = new Student("Jo", 91);

    Alex.RegisterFriend(Technion);
    Jo.RegisterFriend(Technion);
    Technion.PrintAllStudentsGPS();

    Console.ReadLine();
}

поля,все классы могут получить к ним доступ public.

поля,не все остальные классы могут получить доступ к их private.

(если поля принадлежат (объявлены внутри)базовый класс, затем они protected а)

поля,только их класс владельца может получить к ним доступ private, и не имеют свойств и получить набор методов.

поля, которые только их класс владельца и некоторые другие классы могут получить к ним доступ private и у каждого есть специальный private GET и set методы, а public методы акцию.

некоторые другие классы, которые также могут получить доступ к этим полям будут иметь некоторые private поля делегат типы и специальные public прямые методы.

пример:

class A
{
    private int integer; // In the meantime, it seems that only A can access this integer
    private int GetInteger() // Get method is not public, because we don't want all other classes to use this integer
    {
        return this.integer;
    }
    private void SetInteger(int value) //Set method is not public, because we don't want all other classes to modify this integer
    {
        this.integer = value;
    }
    public void Share(ref B b) //I use the 'ref' keyword, to prevent the 'null' value in this argument, but if you call this method in a constructor of B, or in one of its methods, so you will have to remove the 'ref' keyword, or make overload of the same method without the 'ref' keyword, because 'ref this' is not allowed
    {
        b.DirectGetIntegerAndSetIntegerMethods(this.GetInteger, this.SetInteger);
    }
    public void PrintInteger()
    {
        Console.WriteLine(this.integer);
    }
}
class B //This class can access the 'integer' of A too, i.e. the 'integer' of A is "public" only for B
{
    private Func<int> GetInteger; //Will execute the 'GetInteger' method of A
    private Action<int> SetInteger; //Will execute the 'SetInteger' method of A
    public void DirectGetIntegerAndSetIntegerMethods(Func<int> getInteger, Action<int> setInteger)
    {
        this.GetInteger = getInteger;
        this.SetInteger = setInteger;
    }
    public void Increment()
    {
        this.SetInteger(this.GetInteger() + 1);
    }
}
class Program
{
     static void Main(string[] args)
     {
         A a = new A(); //Created new instance of A, and also new Int32 was initialized inside it and set to its default value 0, but unable to get or set its value, only just print it.
         a.PrintInteger();
         B b = new B(); //Must create new instance of B, in order to change the integer of A to some value. For example, I will use b, to change the integer of a to 3
         a.Share(ref b); //But before the change, I must tell 'a' to share his GetInteger and SetInteger methods to 'b', so 'b' will also be able execute them, through his Func<int> and Action<int> delegates, because GetInteger and SetInteger methods of A are private and cannot be executed directly. 
         for (int i = 0; i < 3; i++)
            b.Increment();
         a.PrintInteger(); //Print the integer of 'a' again after change.
         //Now the output of the console is:
         //0
         //3
     }
}

вы должны знать, что использование ключевого слова "friend" в C++ позволяет некоторым классам долю их частные члены в некоторые другие классы напрямую.

из-за того, что ключевое слово "friend" не существует в C#, классы не могут делиться своими частными полями с некоторыми другими классами напрямую, но есть способ имитировать его действительно, как я показал выше.

знаете ли вы, что ключевое слово "friend" в C++ также может позволить при реализации некоторых функций получить доступ к закрытым членам некоторых экземпляров некоторых типов классов?

ответ: да, и я покажу вам, как смоделировать это в C#:

пример:

using System;
using System.Reflection; //New namespace is added
using System.Diagnostics; //Another new namespace is added too

class Person
{
    private readonly StackTrace st = new StackTrace(); //Helper object
    private readonly MethodInfo mi = typeof(Program).GetMethod("Foo"); //The MethodInfo of the method Foo, which will be declared and defined in class Program later, is the friend of the class Person
    //Both objects above are readonly, because they always reference the same objects that they were initialized with.
    private string name_of_his_dog; //Only Person can access this field
    private string GetTheNameOfHisDog() //Not public so that not all methods will be able to get this name
    {
        return this.name_of_his_dog;
    }
    private void SetTheNameOfHisDog(string new_name) //Not public so that not all methods will be able to set this name
    {
        this.name_of_his_dog = new_name;
    }
    public Func<string> ShareTheGetTheNameOfHisDogMethod() //Returns null, if the previous method that called this method is not friend of this class Person
    {
        if (this.st.GetFrame(1).GetMethod() == this.mi)
            return this.GetTheNameOfHisDog;
        return null;
    }
    public Action<string> ShareTheSetTheNameOfHisDogMethod() //Same as above
    {
        if (this.st.GetFrame(1).GetMethod() == this.mi)
            return this.SetTheNameOfHisDog;
        return null;
    }
    public void PrintTheNameOfHisDog()
    {
        Console.WriteLine(this.name_of_his_dog);
    }
}
class Program
{
    static void Main(string[] args)
    {
        Person person = Foo();
        person.PrintTheNameOfHisDog();
        Func<string> getTheNameOfHisDog = person.ShareTheGetTheNameOfHisDogMethod(); //returns null, Main is not friend of Person
        Action<string> setTheNameOfHisDog = person.ShareTheSetTheNameOfHisDogMethod(); //returns null too, for the same reason
       setTheNameOfHisDog("Pointer"); //Runtime Error: Object reference not set to an instance of an object
       Console.WriteLine(getTheNameOfHisDog()); //Same runtime error
       //Output before runtime error:
       //Boxer
       //Boxer
    }
    public static Person Foo() //Only this method can get and set the name of the dog that belongs to the person
    {
        Person person = new Person();
        Func<string> getTheNameOfHisDog = person.ShareTheGetTheNameOfHisDogMethod();
        Action<string> setTheNameOfHisDog = person.ShareTheSetTheNameOfHisDogMethod();
        setTheNameOfHisDog("Boxer");
        Console.WriteLine(getTheNameOfHisDog());
        return person;
    }
}

Я должен признать, что до того, как я разместил этот код, я не знал, как узнать MethodInfo из предыдущего метода, который вызвал текущий метод, но ответ Фираса Ассаада помог мне, благодаря ему тоже.

как я могу найти метод, который вызвал текущий метод?

он предложил используйте System.Diagnostics.StackTrace класс

надеемся, что вы получили мою идею, и это помогает и отвечает на ваш вопрос.

Я не нашел этого ответа нигде в Интернете, я думал об этой идее сам, используя свой мозг.