Dlaczego C# nie zawiera słowa kluczowego "przyjaciel" w stylu C++? [zamknięte]

zamknięte . To pytanie jest oparte na opinii . Obecnie nie przyjmuje odpowiedzi.

chcesz poprawić to pytanie? Update the question so it można odpowiedzieć faktami i cytatami przez edytując ten post .

Zamknięte 3 lata temu.

Popraw to pytanie

Słowo kluczowe C++ friend pozwala class A wyznaczyć class B jako swojego przyjaciela. To pozwala Class B uzyskać dostęp do private/protected Członkowie class A.

I ' ve nigdy nie przeczytałem, dlaczego to zostało pominięte w C #(i VB.NET Większość odpowiedzi na to wcześniejsze pytanie StackOverflow wydaje się mówić, że jest to przydatna część C++ i istnieją dobre powody, aby go używać. Z mojego doświadczenia muszę się zgodzić.

Kolejne pytanie wydaje mi się, że naprawdę zadaje mi pytanie, Jak zrobić coś podobnego do friend w aplikacji C#. Podczas gdy odpowiedzi Zwykle obracają się wokół klas zagnieżdżonych, nie wydaje się to tak eleganckie, jak użycie friend słowo kluczowe.

Oryginalny Design Patterns book używa go regularnie w swoich przykładach.

Podsumowując, dlaczego friend brakuje w C# i jaki jest "najlepszy sposób" (lub sposoby) symulowania go w C#?

(nawiasem mówiąc, internal słowo kluczowe to Nie to samo, pozwala wszystkim klasom w całym zgromadzeniu na dostęp internal członków, podczas gdy friend pozwala dać pewnej klasie pełny dostęp do dokładnie one other class)

Author: Community, 2008-10-15

22 answers

Posiadanie przyjaciół w programowaniu jest mniej więcej uważane za "brudne" i łatwe do nadużywania. Łamie relacje między klasami i podważa niektóre podstawowe atrybuty języka OO.

To powiedziawszy, jest to fajna funkcja i sam używałem jej wiele razy w C++; i chciałbym jej używać również w C#. Ale założę się, że z powodu C#'S "czysty" OOness (w porównaniu do C++'S pseudo ooness) MS zdecydował, że ponieważ Java nie ma przyjaciela słowa kluczowego C# też nie powinien (tylko żart ;))

Na poważnie: wewnętrzny nie jest tak dobry jak przyjaciel, ale robi robotę. Pamiętaj, że rzadko zdarza się, że będziesz rozprowadzać swój kod do 3rd party programistów nie za pośrednictwem DLL; tak długo, jak ty i twój zespół wiedzą o klasach wewnętrznych i ich wykorzystanie powinno być w porządku.

EDIT pozwól mi wyjaśnić, w jaki sposób słowo kluczowe friend podważa OOP.

Prywatne i chronione zmienne i metody są prawdopodobnie jedną z najważniejszych części OOP. Pomysł to, że obiekty mogą przechowywać dane lub logikę, których tylko one mogą używać, pozwala na pisanie implementacji funkcjonalności niezależnie od środowiska - i że środowisko nie może zmieniać informacji o stanie, do których nie nadaje się. Używając friend łączysz ze sobą implementacje dwóch klas - co jest o wiele gorsze, jeśli po prostu połączyłeś ich interfejs.

 68
Author: AustinWBryan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-07-24 10:52:49

Na marginesie. Korzystanie z friend nie polega na naruszeniu enkapsulacji, a wręcz przeciwnie-na jej egzekwowaniu. Jak accessors+mutators, operatory overloading, public inheritance, downcasting, itd., jest często nadużywane, ale nie oznacza to, że słowo kluczowe nie ma, lub, co gorsza, złego celu.

Zobacz wiadomość Konrada Rudolpha w innym wątku, lub jeśli wolisz zobaczyć odpowiedni wpis W C++ FAQ.

 105
Author: Luc Hermitte,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:34:30

Dla informacji, inną powiązaną-ale-nie-całkiem-tą-rzeczą w. NET jest [InternalsVisibleTo], która pozwala asemblowi wyznaczyć inny asembler (taki jak asembler testowy), który (efektywnie) ma "wewnętrzny" dostęp do typów/członków w oryginalnym asemblerze.

 50
Author: Marc Gravell,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-10-15 05:41:25

Powinieneś być w stanie osiągnąć te same rzeczy, do których "przyjaciel" jest używany w C++, używając interfejsów w C#. Wymaga to wyraźnego zdefiniowania, które elementy są przekazywane między dwiema klasami, co jest dodatkową pracą, ale może również ułatwić zrozumienie kodu.

Jeśli ktoś ma przykład rozsądnego użycia "przyjaciela", którego nie można symulować za pomocą interfejsów, proszę o podzielenie się nim! Chciałbym lepiej zrozumieć różnice między C++ i C#.

 13
Author: Parappa,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-10-15 03:40:04

Z friend projektant C++ ma precyzyjną kontrolę nad tym, na kogo są narażeni członkowie*. Ale jest zmuszony zdemaskować każdego z prywatnych członków.

Z internal projektant C# ma precyzyjną kontrolę nad zestawem prywatnych członków, które ujawnia. Oczywiście, może zdemaskować tylko jednego prywatnego członka. Ale będzie narażony na wszystkie klasy w Zgromadzeniu.

Zazwyczaj projektant chce ujawnić tylko kilka prywatnych metod wybranym kilku innym klasom. Na przykład, w klasie factory wzorzec może być pożądane, że Klasa C1 jest instancjonowana tylko przez klasę factory CF1. Dlatego Klasa C1 może mieć chroniony konstruktor i przyjacielską klasę fabryczną CF1.

Jak widzisz, mamy 2 wymiary, wzdłuż których hermetyzacja może być naruszona. friend łamie go wzdłuż jednego wymiaru, internal robi to wzdłuż drugiego. Który z nich jest gorszym naruszeniem koncepcji enkapsulacji? Trudno powiedzieć. Ale byłoby miło mieć zarówno friend jak i internal dostępne. Co więcej, dobrym dodatkiem do tych dwóch jest trzeci typ słowa kluczowego, które będzie używane na zasadzie member-by-member (jak internal) i określa klasę docelową (jak friend).

* dla zwięzłości użyję "private" zamiast "private i / lub protected".

- Nick

 13
Author: Nick Alexeev,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-01-02 16:30:24

W rzeczywistości, C# daje możliwość uzyskania tego samego zachowania w czysty OOP sposób bez specjalnych słów - to prywatne interfejsy.

Jeśli chodzi o pytanie jaki jest odpowiednik C # friend? został oznaczony jako DUPLIKAT tego artykułu i nikt tam nie proponuje naprawdę dobrej realizacji - tutaj pokażę odpowiedź na oba pytania.

Główną ideą było wzięcie stąd: co to jest prywatny interfejs?

Powiedzmy, że potrzebujemy jakiejś klasy, która mogłaby zarządzać instancjami innego klasy i wywołać na nich specjalne metody. Nie chcemy dawać możliwości wywoływania tych metod innym klasom. Jest to dokładnie to samo, co słowo kluczowe friend C++ robi w świecie c++.

Myślę, że dobrym przykładem w praktyce może być Full State Machine pattern, gdzie jakiś kontroler aktualizuje bieżący obiekt stanu i w razie potrzeby przełącza się na inny obiekt stanu.

Możesz:
  • najprostszy i najgorszy sposób na UPDATE () metoda publiczna-nadzieja wszyscy zrozum, dlaczego jest źle.
  • następnym sposobem jest oznaczyć go jako wewnętrzny. Wystarczy, że umieścisz swój klas do innego zgromadzenia, ale nawet wtedy każda klasa w tym zgromadzeniu może wywołać każdą wewnętrzną metodę.
  • użyj prywatnego / chronionego interfejsu - i poszedłem w ten sposób.

Kontroler.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();
    }
}

Program.cs

class Program
{
    static void Main(string[] args)
    {
        //it's impossible to write Controller.IState p = new Controller.StateBase();
        //Controller.IState is hidden
        var p = new Controller.StateBase();
        //p.Update(); //is not accessible
    }
}
A co ze spadkiem?

Musimy użyć techniki opisanej w ponieważ interfejs jawny implementacje member nie mogą być zadeklarowane jako wirtualne i oznaczać Itate jako chronione, aby umożliwić czerpanie z kontrolera.

Kontroler.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()");
    }
}

I na koniec przykład jak przetestować dziedziczenie rzutów kontrolera klasy: ControllerTest.cs

class ControllerTest: Controller
{
    public ControllerTest()
    {
        IState testObj = new PlayerIdleState();
        testObj.Update();
    }
}
Mam nadzieję, że omówię wszystkie sprawy i moja odpowiedź była przydatna.
 13
Author: max_cn,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-04-24 13:27:56

Możesz zbliżyć się do C++ "przyjaciel" za pomocą słowa kluczowego C# "Wewnętrzny" .

 8
Author: jeffm,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-10-15 03:28:55

Friend jest niezwykle przydatny przy pisaniu testu jednostkowego.

Choć wiąże się to z niewielkim zanieczyszczeniem deklaracji klasy, jest to również wymuszone przez kompilator przypomnienie o tym, jakie testy faktycznie mogą dbać o wewnętrzny stan klasy.

Bardzo użytecznym i czystym idiomem, który znalazłem, jest to, że mam klasy fabryczne, czyniące je przyjaciółmi przedmiotów, które tworzą, które mają chroniony konstruktor. Dokładniej, to było wtedy, gdy miałem jedną fabrykę odpowiedzialną za tworzenie dopasowanych obiektów renderujących dla obiektów report writer, renderowanie do danego środowiska. W tym przypadku masz jeden punkt wiedzy na temat relacji między klasami report-writer (rzeczy takie jak bloki obrazów, pasma układu, nagłówki stron itp.) i dopasowujących się do nich obiektów renderujących.

 8
Author: Andy Dent,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-01-19 16:27:31

W C# brakuje słowa kluczowego "przyjaciel" z tego samego powodu, dla którego brakuje deterministycznej destrukcji. Zmiana konwencji sprawia, że ludzie czują się mądrzy, jakby ich nowe sposoby były lepsze od starych sposobów kogoś innego. Chodzi o dumę.

Stwierdzenie, że "friend classes are bad" jest tak krótkowzroczne, jak inne niewykwalifikowane stwierdzenia, takie jak" don 't use gotos" lub "Linux jest lepszy niż Windows".

Słowo kluczowe "przyjaciel" w połączeniu z klasą proxy jest świetnym sposobem na ujawnienie tylko pewnych części klasy do określonych innych klas. Klasa proxy może działać jako zaufana bariera w stosunku do wszystkich innych klas. "publiczny" nie pozwala na takie targetowanie, a używanie "chronionego", aby uzyskać efekt dziedziczenia, jest niezręczne, jeśli naprawdę nie ma pojęciowego związku" jest".

 8
Author: Matthew,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-04-09 05:52:54

To nie jest problem z C#. To podstawowe ograniczenie w IL. C# jest przez to Ograniczony, podobnie jak każdy inny język. Net, który stara się być weryfikowalny. Ograniczenie to obejmuje również klasy zarządzane zdefiniowane w C++ / CLI (Spec section 20.5).

To powiedziawszy, myślę, że Nelson ma dobre wytłumaczenie, dlaczego to jest złe.

 7
Author: JaredPar,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-10-15 08:07:42

Przestań usprawiedliwiać to ograniczenie. przyjaciel jest zły, ale wewnętrzny jest dobry? to jest to samo, tylko, że przyjaciel daje Ci bardziej precyzyjną kontrolę nad tym, kto ma dostęp, a kto nie.

To ma wymusić paradygmat enkapsulacji? więc musisz napisać metody accessor i co teraz? jak masz powstrzymać wszystkich (poza metodami klasy B) przed wywołaniem tych metod? nie możesz, bo tego też nie możesz kontrolować, bo brakuje "przyjaciel".

Żaden język programowania nie jest doskonały. C# jest jednym z najlepszych języków, jakie widziałem, ale wymyślanie głupich wymówek dla brakujących funkcji nikomu nie pomaga. W C++ brakuje mi łatwego systemu event / delegate, reflection (+automatyczna de/serializacja) i foreach, ale w C# brakuje mi przeciążenia operatora( tak, mów mi, że go nie potrzebujesz), domyślnych parametrów, const, którego nie można obejść, wielokrotnego dziedziczenia (tak, mów mi, że go nie potrzebujesz i interfejsów jest to wystarczająca wymiana) i możliwość podjęcia decyzji o usunięciu instancji z pamięci (Nie, Nie Jest to strasznie złe, chyba że jesteś majsterkowiczem)

 4
Author: Algoman,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-11-15 07:41:53

Odpowiem tylko na pytanie "jak".

Jest tu tak wiele odpowiedzi, jednak chciałbym zaproponować rodzaj "wzorca projektowego", aby osiągnąć tę funkcję. Użyję prostego mechanizmu językowego, który obejmuje:

  • interfejsy
  • Klasa zagnieżdżona

Na przykład mamy 2 główne klasy: Student i Uniwersytet. Student ma średnią ocen, do której dostęp ma tylko Uniwersytet. Oto kod:

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();
}
 3
Author: Rami Yampolsky,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-07-24 10:59:28

Istnieje InternalsVisibleToAttribute od. Net 3, ale podejrzewam, że dodali go dopiero po pojawieniu się testów jednostkowych. Nie widzę innych powodów, by z niego korzystać.

Działa na poziomie złożenia, ale wykonuje zadanie tam, gdzie wewnętrzne nie; to znaczy, gdzie chcesz rozpowszechniać zestaw, ale chcesz, aby inny nie rozproszony zestaw miał uprzywilejowany dostęp do niego.

Całkiem słusznie wymagają, aby zespół przyjaciela był silny, aby uniknąć kogoś, kto tworzy udawanego przyjaciela obok Twojego chronionego zespołu.

 2
Author: Chris Woodward,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-07-11 21:25:53

Przeczytałem wiele mądrych komentarzy na temat słowa kluczowego "przyjaciel" i zgadzam się, co to jest przydatna rzecz, ale myślę, że to, co" wewnętrzne " słowo kluczowe jest mniej przydatne , i oba nadal złe dla czystego programowania OO.

Co mamy? (mówiąc o" przyjacielu "mówię również o "wewnętrznym")
  • czy użycie "przyjaciela" czyni kod mniej czystym w odniesieniu do oo?
  • Tak;

  • Czy użycie "przyjaciela" czyni kod lepszym?

  • nie, nadal musimy zrobić coś prywatnego relacje między klasami i możemy to zrobić tylko wtedy, gdy złamiemy naszą piękną enkapsulację, więc to również nie jest dobre, mogę powiedzieć, co to jeszcze bardziej złe niż użycie "przyjaciela".

Używanie friend powoduje problemy lokalne, a nie używanie go powoduje problemy dla użytkowników bibliotek kodu.

Wspólne dobre rozwiązanie dla języka programowania widzę tak:

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

// c#
class Foo {
    public_for Bar void addBar(Bar bar) { }
}
Co o tym myślisz? Myślę, że jest to najbardziej powszechne i czyste rozwiązanie zorientowane obiektowo. Możesz otworzyć dostęp dowolną metodą wybierz dowolną klasę, którą chcesz.
 2
Author: FadeToBlack,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-10-21 09:22:04

Podejrzewam, że ma to coś wspólnego z modelem kompilacji C# -- budowanie IL kompilacji JIT w czasie wykonywania. tj.: ten sam powód, dla którego C# generics zasadniczo różni się od C++ generics.

 1
Author: Stewart Johnson,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-10-15 03:29:59

Możesz zachować prywatność i użyć reflection do wywołania funkcji. Framework testowy może to zrobić, jeśli poprosisz go o przetestowanie funkcji prywatnej

 1
Author: vishal,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-06-04 13:24:44

Kiedyś regularnie korzystałem z friend, i nie sądzę, aby to było naruszenie OOP lub oznaka jakiejkolwiek wady konstrukcyjnej. Istnieje kilka miejsc, w których jest to najbardziej efektywny środek do właściwego końca z najmniejszą ilością kodu.

Jednym z konkretnych przykładów jest tworzenie zestawów interfejsów, które zapewniają interfejs komunikacyjny do innego oprogramowania. Generalnie istnieje kilka klas wagi ciężkiej, które zajmują się złożonością protokołu i osobliwości rówieśniczych, i zapewniają stosunkowo prosty model connect / read/write / forward / disconnect obejmujący przekazywanie wiadomości i powiadomień między aplikacją kliencką a zespołem. Te wiadomości / powiadomienia muszą być owinięte w klasy. Atrybuty zazwyczaj muszą być manipulowane przez oprogramowanie protokołu, ponieważ jest ich twórcą, ale wiele rzeczy musi pozostać tylko do odczytu dla świata zewnętrznego.

Po prostu głupotą jest stwierdzenie, że to naruszenie OOP dla klasy protocol / "creator", aby mieć intymny dostęp do wszystkie utworzone klasy -- Klasa creator musiała bitować munge ' a każdy bit danych po drodze. Najważniejsze jest zminimalizowanie wszystkich dodatkowych linii kodu, do których zazwyczaj prowadzi model "OOP for OOP 's Sake". Dodatkowe spaghetti robi więcej robaków.

Czy ludzie wiedzą, że można zastosować wewnętrzne słowo kluczowe na poziomie atrybutu, właściwości i metody? Nie jest to tylko deklaracja klasy najwyższego poziomu (choć większość przykładów zdaje się to pokazywać.)

Jeśli masz Klasa C++, która używa słowa kluczowego friend i chce emulować to w klasie C# : 1. declare the C# class public 2. zadeklaruj wszystkie atrybuty / właściwości / metody, które są chronione w C++ i tym samym dostępne dla znajomych jako wewnętrzne w C# 3. tworzenie właściwości tylko do odczytu dla publicznego dostępu do wszystkich wewnętrznych atrybutów i właściwości

Zgadzam się, że nie jest to w 100% to samo co friend, a test jednostkowy jest bardzo cennym przykładem potrzeby czegoś takiego jak friend (podobnie jak protokół analyzer logging kod). Jednak internal zapewnia ekspozycję na klasy, które chcesz mieć ekspozycję, a [InternalVisibleTo()] zajmuje się resztą-wygląda na to, że została stworzona specjalnie do testów jednostkowych.

Jeśli chodzi o friend "bycie lepszym, ponieważ można wyraźnie kontrolować, które klasy mają dostęp" - Co u licha robią podejrzane złe klasy w tym samym zbiorze? Podziel swoje zespoły!

 1
Author: Christo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-01-30 09:41:40

Przyjaźń może być symulowana przez oddzielenie interfejsów i implementacji. Idea jest następująca: "Wymagaj konkretnej instancji, ale ograniczaj dostęp do budowy tej instancji ".

Na przykład

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) { }
}

Pomimo faktu, że ItsMeYourFriend() jest publiczna tylko Friend klasa może uzyskać do niej dostęp, ponieważ nikt inny nie może uzyskać konkretnej instancji klasy Friend. Posiada prywatny konstruktor, podczas gdy metoda factory New() zwraca interfejs.

Zobacz mój artykuł przyjaciele i internal interface members bez kosztów z kodowaniem do interfejsów dla szczegółów.

 1
Author: Reuven Bass,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-06-21 19:47:40

Niektórzy sugerują, że rzeczy mogą wymknąć się spod kontroli za pomocą przyjaciela. Zgadzam się, ale to nie zmniejsza jego przydatności. Nie jestem pewien, czy przyjaciel koniecznie rani paradygmat OO bardziej niż upublicznianie wszystkich członków twojej klasy. Z pewnością język pozwoli Ci upublicznić wszystkich swoich członków, ale jest to zdyscyplinowany programista, który unika tego typu wzorca projektowego. Podobnie zdyscyplinowany programista zastrzega sobie użycie friend dla konkretnych przypadków, w których sprawia, że sens. Czuję, że w niektórych przypadkach zbyt wiele się ujawnia. Po co wystawiać klasę lub metodę na wszystko w zespole?

Mam ASP.NET strona, która dziedziczy moją własną stronę bazową, która z kolei dziedziczy System.Www.UI.Strona. Na tej stronie Mam kod, który obsługuje raportowanie błędów użytkownika końcowego dla aplikacji w metodzie protected

ReportError("Uh Oh!");

Teraz mam kontrolę użytkownika, która jest zawarta na stronie. Chcę, aby kontrola użytkownika mogła wywoływać metody raportowania błędów na stronie.

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

Nie może tego zrobić, jeśli metoda ReportError jest chroniona. Mogę to zrobić wewnętrznie, ale jest wystawione na działanie dowolnego kodu w zespole. Chcę tylko, aby była wystawiona na elementy interfejsu użytkownika, które są częścią bieżącej strony (w tym kontrolki potomne). Dokładniej, chcę, aby moja klasa kontrolna base definiowała dokładnie te same metody raportowania błędów i po prostu wywoływała metody na stronie base.

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

Uważam, że coś takiego jak przyjaciel może być przydatne i zaimplementowane w języku nie czyniąc języka mniej "OO" jak, być może jako atrybuty, dzięki czemu można mieć klasy lub metody zaprzyjaźnić się z konkretnymi klasami lub metodami, co pozwala deweloperowi zapewnić bardzo konkretny dostęp. Może coś w tym stylu...(pseudo code)

[Friend(B)]
class A {

    AMethod() { }

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

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

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

W przypadku mojego poprzedniego przykładu może mieć coś takiego jak poniżej (można argumentować semantykę, ale staram się tylko przekazać pomysł):

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);
    }
}

Jak widzę, koncepcja przyjaciela nie wiąże się z większym ryzykiem niż zrobienie rzeczy publiczne lub tworzenie publicznych metod lub właściwości w celu uzyskania dostępu do członków. Jeśli cokolwiek przyjaciel pozwala na inny poziom szczegółowości w dostępności danych i pozwala zawęzić tę dostępność, a nie rozszerzać ją o wewnętrzne lub publiczne.

 1
Author: Scott Forbes,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-07-18 20:09:38

Jeśli pracujesz z C++ i odnajdujesz siebie używając słowa kluczowego friend, jest to bardzo silne wskazanie, że masz problem z projektowaniem, bo po cholerę klasa musi mieć dostęp do prywatnych członków innej klasy??

 0
Author: bashmohandes,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-10-15 05:28:24

B. s. d.

Stwierdzono, że przyjaciele ranią czystą OOness. Zgadzam się.

Stwierdzono również, że przyjaciele pomagają enkapsulacji, co też się Zgadzam.

Myślę, że przyjaźń powinna być dodana do metodyki OO, ale nie do końca tak jak w C++. Chciałbym mieć niektóre pola / metody, do których moja klasa przyjaciół może uzyskać dostęp, ale nie chciałbym, aby miały dostęp do wszystkich moich pól / metod. Jak w prawdziwym życiu, pozwoliłbym moim znajomym uzyskać dostęp do mojej osobistej lodówki, ale nie pozwoliłbym im uzyskać dostępu do mojego banku konto.

Można to zaimplementować w następujący sposób

    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);
        }
    }

To oczywiście wygeneruje Ostrzeżenie kompilatora i zaszkodzi intellisense. Ale da radę.

Na marginesie, myślę, że pewien programista powinien wykonać testowanie bez dostępu do prywatnych członków. to jest dość poza zakresem, ale spróbuj poczytać o TDD. jednak, jeśli nadal chcesz to zrobić (mając c++ jak przyjaciele) spróbuj czegoś takiego jak

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

Więc piszesz cały swój kod bez definiowania UNIT_TESTING i gdy chcesz wykonać Testowanie jednostek, dodaj #define UNIT_TESTING do pierwszej linii pliku(i zapisz cały kod, który wykonuje Testowanie jednostek pod #if UNIT_TESTING). To powinno być traktowane ostrożnie.

Ponieważ uważam, że testowanie jednostkowe jest złym przykładem wykorzystania przyjaciół, podałbym przykład, dlaczego uważam, że przyjaciele mogą być dobrzy. Załóżmy, że masz system łamania (Klasa). Przy użyciu System łamania się zużywa i trzeba go odnowić. Teraz ty chcesz, żeby tylko licencjonowany mechanik to naprawił. Aby przykład był mniej trywialny, powiedziałbym, że mechanik użyłby swojego osobistego (prywatnego) śrubokręta, aby go naprawić. Dlatego Klasa mechanik powinien być przyjacielem klasy breakingSystem.

 0
Author: ehud117,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-07-05 11:28:07

Przyjaźń może być również symulowana za pomocą "agentów" - pewnych klas wewnętrznych. Rozważ następujący przykład:

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.
  }
}

Może być dużo prostsze, gdy jest używane tylko do dostępu do statycznych elementów. Korzyści dla takiej implementacji polega na tym, że wszystkie typy są deklarowane w wewnętrznym zakresie klas przyjaźni i, w przeciwieństwie do interfejsów, umożliwia dostęp do statycznych członków.

 0
Author: Aberro,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-01-02 16:43:15