C # neden C ++ stili 'arkadaş' anahtar kelimesini sağlamaz? [kapalı]


208

C ++ arkadaşı anahtar bir olanak sağlar class Abelirtmek için class Bkendi arkadaş olarak. Bu, sitesinin / üyelerine Class Berişilmesini sağlar .privateprotectedclass A

Bunun neden C # (ve VB.NET) dışında kaldığına dair hiçbir şey okumadım. Bu daha önceki StackOverflow sorusuna cevapların çoğu , C ++'ın yararlı bir parçası olduğunu ve bunu kullanmanın iyi nedenleri olduğunu söylüyor. Benim tecrübelerime katılıyorum.

Başka bir soru bana gerçekten friendbir C # uygulamasında benzer bir şey yapmak nasıl soruyor gibi görünüyor . Cevaplar genellikle iç içe sınıflar etrafında dönmesine rağmen, friendanahtar kelimeyi kullanmak kadar zarif görünmüyor .

Orijinal Tasarım Desenleri kitabı , örnekleri boyunca düzenli olarak kullanır.

Özet olarak, neden friendC # eksik ve C # simüle "en iyi uygulama" yolu (veya yolları) nedir?

(Bu arada, internalanahtar kelime aynı şey değildir , tüm derleme içindeki tüm sınıfların internalüyelere erişmesine friendizin verirken , belirli bir sınıfa tam olarak bir başka sınıfa tam erişim izni verir )


5
iç korumalı hissediyorum iyi bir uzlaşma olduğunu ..
Gulzar Nazim

3
Çok kafa karıştırıcı değil mi? Yukarıda söylediğim gibi GOF kitabı bunu örneklerde oldukça sık kullanıyor. Bana göre bu içsel olmaktan çok kafa karıştırıcı değil.
Kül

7
Daha önce de bahsettiğimiz gibi Birim Testlerinde de çok yararlı olabilecek senaryoları görebiliyorum. Peki arkadaşlarınızın sizin özel kişilerinize erişmesini istiyor musunuz?
danswain

2
Cevaplamak kolaydır: C #, aynı modül / montajdaki tüm koda erişim sağlayan erişim değiştirici olarak "dahili" sunar. Bu, arkadaş gibi bir şeye olan ihtiyacı ortadan kaldırır. Java'da "korumalı" anahtar kelimesi, aynı paketten benzer şekilde erişir.
sellibitze

2
@ sellibitze, ben soru ile iç arasındaki farklardan söz ediyorum. Interanl ile ilgili sorun derlemedeki tüm sınıfların iç üyelere erişebilmesidir. Bu sınıfların birçoğunun erişime ihtiyacı olmayabileceğinden, bu kapsüllemeyi keser.
Ash

Yanıtlar:


67

Programlamada arkadaşlara sahip olmak az ya da çok "kirli" kabul edilir ve kötüye kullanımı kolaydır. Sınıflar arasındaki ilişkileri koparır ve bir OO dilinin bazı temel özelliklerini zayıflatır.

Olduğu söyleniyor, güzel bir özellik ve ben C ++ kendim birçok kez kullandım; ve C # 'da da kullanmak istiyorum. Ama C # 's "saf" OOness (C ++' ın sahte OOness ile karşılaştırıldığında) nedeniyle bahis MS Java hiçbir arkadaş anahtar kelime C # çünkü (sadece şaka;) olmamalıdır karar verdi

Ciddi bir not: iç arkadaş kadar iyi değil ama işi hallediyor. Kodunuzu bir DLL aracılığıyla değil, üçüncü taraf geliştiricilere dağıtmak nadir olduğunu unutmayın; böylece siz ve ekibiniz iç sınıfları ve bunların kullanımlarını bildiğiniz sürece iyi olmalısınız.

EDIT Arkadaş anahtar kelimesinin OOP'u nasıl zayıflattığını açıklayayım.

Özel ve korunan değişkenler ve yöntemler belki OOP'un en önemli kısımlarından biridir. Nesnelerin yalnızca kullanabilecekleri veri veya mantık tutabileceği fikri, çevrenizden bağımsız olarak işlevsellik uygulamanızı yazmanıza olanak tanır - ve ortamınız işlemek için uygun olmayan durum bilgilerini değiştiremez. Arkadaşınızı kullanarak iki sınıfın uygulamalarını birbirine bağlıyorsunuz - bu sadece arayüzlerini birleştirdiğinizde çok daha kötü.


167
C # 'internal' aslında kapsüllemeyi C ++ 'friend' den çok daha fazla incitiyor. "Dostluk" ile, sahibi istediği kimseye izin verir. "İç" açık uçlu bir kavramdır.
Nemanja Trifunovic

21
Anders, içerdiği özellikler dışında bıraktığı özellikler için övülmelidir.
Mehrdad Afshari

21
C # 'ın iç olsa da enkapsülasyon acıyor katılmıyorum. Bence tam tersi. Bir montaj içinde kapsüllenmiş bir ekosistem oluşturmak için kullanabilirsiniz. Birkaç nesne, bu ekosistemde uygulamanın geri kalanına maruz kalmayan iç türleri paylaşabilir. Bu nesneler için birim sınama derlemeleri oluşturmak için "arkadaş" derleme hilelerini kullanıyorum.
09:11

26
Bu mantıklı değil. friendkeyfi sınıfların veya işlevlerin özel üyelere erişmesine izin vermez. Arkadaş olarak işaretlenen belirli işlevlerin veya sınıfların bunu yapmasına izin verir. Özel üyeye sahip olan sınıf hâlâ ona erişimi% 100 kontrol ediyor. Kamu üyesi yöntemlerini de tartışabilirsiniz. Her ikisi de sınıfta özel olarak listelenen yöntemlerin sınıfın özel üyelerine erişiminin olmasını sağlar.
jalf

8
Bence tam tersi. Bir derleme 50 sınıf varsa, bu sınıflardan 3'ü bazı yardımcı sınıf kullanırsa, bugün tek seçeneğiniz bu yardımcı programı sınıfını "dahili" olarak işaretlemektir. Bunu yaparken, sadece 3 sınıf için değil, aynı zamanda montajdaki diğer sınıflar için de kullanılabilir hale getirmiş olursunuz. Tek yapmanız gereken, yalnızca söz konusu üç sınıfın yardımcı program sınıfına erişimi olacak şekilde daha hassas erişim sağlamaktır. Kapsüllemeyi geliştirdiği için aslında onu daha nesne yönelimli yapıyor.
zumalifeguard

104

Yan notta. Arkadaş kullanmak kapsüllemeyi ihlal etmekle ilgili değil, aksine onu uygulamakla ilgilidir. Erişgeçtir + mutators, aşırı operatörler, kamu miras, downcasting gibi, vb , genellikle yanlış, ama söylenenlerin kelime kötü bir amaç, hayır, ya da kötü olduğu anlamına gelmez.

Bkz Konrad Rudolph 'ın mesajı diğer thread, ya da tercih olmadığını görmek alakalı girişini C ++ SSS.


... ve FQA'daki
Josh Lee

28
+1 için " Arkadaş kullanmak kapsüllemeyi ihlal etmekle ilgili değil, aksine onu uygulamakla ilgili "
Nawaz

2
Semantik bir görüş meselesi bence; ve belki de söz konusu durumla ilgili. Bir sınıf yapmak friend, sadece bir tanesini belirlemesine izin vermeniz gerekse bile, TÜM özel üyelerinize erişmesini sağlar. Alanların, başka bir sınıfa ihtiyaç duymadan kullanılabilir hale getirilmesinin "kapsülleme ihlali" olarak kabul edildiğine dair güçlü bir argüman vardır. C ++ 'da gerekli olan yerler vardır (aşırı yük operatörleri), ancak dikkatle düşünülmesi gereken bir kapsülleme geliri vardır. Görünüşe göre C # tasarımcıları ödünleşmenin buna değmediğini hissettiler.
GrandOpener

3
Kapsülleme, değişmezleri zorlamakla ilgilidir. Bir sınıfı bir diğerine arkadaş olarak tanımladığımızda, açık bir şekilde iki sınıfın birincisinin değişmezlerinin yönetimi ile yakından ilişkili olduğunu söylüyoruz. Sınıfın arkadaş edinmesi, tüm nitelikleri doğrudan (kamusal alan olarak) veya ayarlayıcılar aracılığıyla göstermekten daha iyidir.
Luc Hermitte

1
Kesinlikle. Arkadaşınızı kullanmak istediğinizde, ancak kullanamayacağınız zaman, içsel veya kamusal olarak kullanmak zorundasınız, ki bu da yapmamaları gereken şeylere karşı dürüst olmaya daha açıktır. Özellikle ekip ortamında.
Kuluçkahane

50

Bilgi için, .NET'te aynı olan ancak aynı olmayan başka bir şey [InternalsVisibleTo], bir derlemenin türlere / üyelere (etkin olarak) "iç" erişime sahip başka bir derleme (bir birim sınama derlemesi gibi) belirlemesine izin verir. orijinal montajı.


3
iyi bir ipucu, muhtemelen arkadaş arayan insanlar daha fazla içsel 'bilgi' yeteneğine sahip birim testi yapmak için bir yol bulmak ister.
SwissCoder

14

C ++ 'da arayüzler kullanarak "arkadaş" C ++ için kullanılan şeyleri aynı türlerini başarmak gerekir. İki sınıf arasında hangi üyelerin geçirildiğini açıkça tanımlamanızı gerektirir, bu da ekstra bir iştir, ancak kodun daha kolay anlaşılmasını sağlayabilir.

Birisinin arayüzler kullanılarak simüle edilemeyen makul bir "arkadaş" kullanımı örneği varsa, lütfen paylaşın! C ++ ve C # arasındaki farkları daha iyi anlamak istiyorum.


İyi bir nokta. Daha önce SO soruya bakmak için bir şans var mı (C # o çözmek için monoksit tarafından sorulan ve nasıl en iyi şekilde ilgilenen olurdu ?. yukarıda bağlantılı?
Kül

Tam olarak nasıl C # yapmak emin değilim, ama Jon Skeet monoksit soru noktalarına doğru yönde cevap düşünüyorum. C # iç içe sınıflara izin verir. Aslında kod yazmayı ve bir çözüm derlemeyi denemedim. :)
Parappa

Arabuluculuk düzeninin, arkadaşın güzel olacağına iyi bir örnek olduğunu hissediyorum. Bir aracıya sahip olmak için Formlarımı yeniden düzenlerim. Formumun "olayı" arabulucudaki yöntemler gibi çağırabilmesini istiyorum, ama diğer sınıfları bu "olay" gibi yöntemler olarak adlandırmak istemiyorum. "Arkadaş" özelliği, forma, derlemedeki diğer her şeye açarak yöntemlere erişim sağlar.
Vaccano

3
'Friend' değiştirmenin arayüz yaklaşımıyla ilgili sorun, bir nesnenin bir arayüzü ortaya çıkarmasıdır, yani herkes nesneyi o arayüze atabilir ve yöntemlere erişebilir! Arayüzü dahili, korumalı veya özel olarak kapsamlandırmak ya işe yaramış gibi çalışmaz, sadece söz konusu yöntemi kapsamlayabilirsiniz! Bir alışveriş merkezinde bir anne ve çocuk düşünün. Kamu dünyadaki herkes. Dahili sadece alışveriş merkezinde insanlar. Özel, sadece anne ya da çocuktur. 'Arkadaş' sadece anne ve kızı arasında bir şeydir.
Mark A. Donohoe

1
İşte bir tane: stackoverflow.com/questions/5921612/… C ++ geçmişinden geldiğim için, arkadaş eksikliği beni neredeyse her gün deli ediyor. C # ile geçirdiğim birkaç yıl boyunca, yüzlerce yöntemi herkese özel ve daha güvenli olabilecekleri, herkese arkadaş olmamasından dolayı halka açtım. Şahsen arkadaşın
.NET'e

14

İle friendbir C ++ tasarımcı özel * üyeleri maruz kime üzerinde kesin kontrol vardır. Ancak, özel üyelerin her birini ortaya çıkarmak zorunda kaldı.

İle internalbir C # tasarımcısı diye teşhir ediyor özel üyelerin kümesi üzerinde kesin kontrolü vardır. Açıkçası, sadece tek bir özel üyeyi ifşa edebilir. Ancak, meclis içindeki tüm sınıflara maruz kalacaktır.

Tipik olarak, bir tasarımcı seçilen birkaç başka sınıfa sadece birkaç özel yöntem göstermek ister. Örneğin, bir sınıf fabrikası modelinde, C1 sınıfının sadece CF1 sınıf fabrikası tarafından başlatılması istenebilir. Dolayısıyla C1 sınıfı korumalı bir kurucuya ve bir arkadaş sınıfı fabrikası CF1'e sahip olabilir.

Gördüğünüz gibi, kapsüllemenin ihlal edilebileceği 2 boyutumuz var. friendbir boyut boyunca ihlal eder internal, diğer boyut boyunca yapar. Hangisi kapsülleme konseptinde daha kötü bir ihlal? Söylemesi zor. Ama her ikisi de olması güzel olurdu friendve internalmevcut. Ayrıca, bu ikisine iyi bir ek, üye-üye bazında (gibi internal) kullanılacak ve hedef sınıfı (gibi friend) belirten 3. anahtar kelime türü olacaktır .

* Kısacası "özel ve / veya korumalı" yerine "özel" kullanacağım.

- Nick


13

Aslında, C # özel kelimeler olmadan aynı davranışları saf OOP biçiminde elde etme imkanı verir - özel arayüzler.

Bildiğim kadarıyla arkadaş C # eşdeğeri nedir? bu makalenin kopyası olarak işaretlendi ve orada hiç kimse gerçekten iyi farkındalık önermiyor - burada her iki soruya da cevap göstereceğim.

Ana fikir buradan geliyordu: Özel arayüz nedir?

Diyelim ki, başka bir sınıfın örneklerini yönetebilecek ve bunlara bazı özel yöntemler çağırabilecek bir sınıfa ihtiyacımız var. Bu yöntemleri başka bir sınıfa çağırmak istemiyoruz. Bu arkadaş c ++ anahtar kelime c ++ dünyada ne aynı şeydir.

Gerçek pratikte iyi bir örnek, bazı denetleyicinin geçerli durum nesnesini güncellediği ve gerektiğinde başka bir durum nesnesine geçtiği Tam Durum Makinesi deseni olabileceğini düşünüyorum.

Yapabilirdiniz:

  • Update () yöntemini herkese açık hale getirmenin en kolay ve en kötü yolu - umarım herkes bunun neden kötü olduğunu anlar.
  • Bir sonraki yol onu dahili olarak işaretlemektir. Sınıflarınızı başka bir montaja koyarsanız iyi olur, ancak o montajda bile her sınıf her dahili yöntemi çağırabilir.
  • Özel / korumalı arayüz kullanın - ve bu yolu takip ettim.

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

Peki ya miras?

Açık arabirim üyesi uygulamaları sanal olarak bildirilemediğinden ve IState'i Denetleyici'den türetme imkanı vermek için korumalı olarak işaretlediğinden beri açıklanan tekniği kullanmamız gerekir .

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

Ve son olarak sınıf Denetleyici atma mirasının nasıl test edileceğine örnek: ControllerTest.cs

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

Umarım tüm davaları kaparım ve cevabım faydalı oldu.


Bu, daha fazla oy kullanması gereken harika bir cevaptır.
KthProg

@max_cn Bu gerçekten güzel bir çözüm, ancak bunu birim test için alaycı çerçevelerle çalışmanın bir yolunu göremiyorum. Baska öneri?
Steve Land

Biraz kafam karıştı. Harici bir sınıfın ilk örneğinizde (özel arayüz) Update'i arayabilmesini isteseydim, bunun olmasını sağlamak için Denetleyiciyi nasıl değiştirirdim?
AnthonyMonterrosa

@Steve Land, ControllerTest.cs dosyasında gösterildiği gibi kalıtım kullanabilirsiniz. Türetilmiş bir test sınıfına gerekli herkese açık yöntemleri ekleyin, temel sınıfta bulunan tüm korunan personel için erişiminiz olacaktır.
max_cn

@AnthonyMonterrosa, Güncellemeyi TÜM harici kaynaklardan gizliyoruz, bu yüzden neden biriyle paylaşmak istiyorsunuz;)? Elbette özel üyelere erişmek için bazı genel yöntemler ekleyebilirsiniz, ancak diğer sınıflar için bir arka kapı gibi olacaktır. Her neyse, test amacıyla ihtiyacınız varsa - orada test senaryoları yapmak için ControllerTest sınıfı gibi bir şey yapmak için miras kullanın.
max_cn

9

Birim testi yazarken arkadaş son derece kullanışlıdır.

Bu, sınıf beyanınızı hafifçe kirletmenin bir bedeli olsa da, aynı zamanda testlerin sınıfın iç durumu hakkında gerçekte neleri önemsediğinin derleyici tarafından zorlanan bir hatırlatıcısıdır.

Bulduğum çok kullanışlı ve temiz bir deyim, fabrika sınıflarım olduğunda, onları oluşturdukları korumalı bir kurucuya sahip olan öğelerin arkadaşları yapıyor. Daha spesifik olarak, rapor yazar nesneleri için eşleşen oluşturma nesneleri oluşturmaktan ve belirli bir ortama dönüştürmekten sorumlu tek bir fabrikam vardı. Bu durumda, rapor yazar sınıfları (resim blokları, düzen bantları, sayfa başlıkları vb.) Ve bunlarla eşleşen oluşturma nesneleri arasındaki ilişki hakkında tek bir bilgiye sahip olursunuz.


"Arkadaş" ın fabrika dersleri için yararlı olduğu hakkında bir yorum yapacaktım, ama birisinin zaten yaptığını görmek beni mutlu etti.
Edward Robertson

9

C # "deterministic yıkımı eksik aynı nedenle" arkadaş "anahtar sözcüğü eksik. Değişen kurallar, sanki yeni yolları sanki bir başkasının eski yollarından daha üstün gibi, insanları zeki hissettirir. Her şey gururla ilgili.

"Arkadaş sınıfları kötüdür" demek, "gotos kullanma" veya "Linux, Windows'dan daha iyidir" gibi diğer niteliksiz ifadeler kadar kısa görüşlüdür.

Proxy sınıfıyla birleştirilmiş "arkadaş" anahtar kelimesi, bir sınıfın yalnızca belirli bölümlerini belirli sınıflara maruz bırakmanın harika bir yoludur . Proxy sınıfı, diğer tüm sınıflara karşı güvenilir bir engel görevi görebilir. "herkese açık" böyle bir hedeflemeye izin vermez ve gerçekten "kavramsal" bir ilişki yoksa, miras ile etki elde etmek için "korumalı" kullanmak gariptir.


Çöp toplanmasından daha yavaş olduğu için C # 'nin deterministik yıkımı eksik. Deterministik imha, yaratılan her nesne için zaman alırken, çöp toplama işlemi şu anda canlı olan nesneler için zaman alır. Çoğu durumda, bu daha hızlıdır ve sistemde ne kadar kısa ömürlü nesneler varsa, nispeten daha hızlı çöp toplama olur.
Edward Robertson

1
@Edward Robertson Tanımlanmış bir yıkıcı olmadığı sürece deterministik yıkım hiçbir zaman almaz. Ancak bu durumda, çöp toplama çok daha kötüdür, çünkü o zaman daha yavaş ve belirsiz olmayan bir sonlandırıcı kullanmanız gerekir.
kaalus

1
... ve bellek tek kaynak değildir. İnsanlar genellikle doğru gibi davranırlar.
cubuspl42

8

"İç" C # anahtar sözcüğü ile C ++ "arkadaş" yakın alabilirsiniz .


3
Yakın, ama tam olarak orada değil. Meclislerinizi / sınıflarınızı nasıl yapılandırdığınıza bağlı olduğunu düşünüyorum, ancak arkadaşınızı kullanarak erişim verilen sınıf sayısını en aza indirmek daha zarif olacaktır. Bir yardımcı program / yardımcı derlemede örnek olarak, tüm sınıfların dahili üyelere erişimi olmamalıdır.
Kül

3
@Ash: Buna alışmanız gerekiyor, ancak montajları uygulamalarınızın temel yapı taşı olarak gördüğünüzde, internalçok daha mantıklı ve kapsülleme ile yardımcı program arasında mükemmel bir denge sağlar. Bununla birlikte, Java'nın varsayılan erişimi daha da iyidir.
Konrad Rudolph

7

Bu aslında C # ile ilgili bir sorun değildir. IL'de temel bir sınırlama vardır. C #, bunun doğrulanabilir olmasını isteyen diğer .Net dilleri ile sınırlıdır. Bu sınırlama ayrıca C ++ / CLI'de tanımlanan yönetilen sınıfları da içerir ( Spesifikasyon 20.5 ).

Sanırım Nelson'ın bunun neden kötü bir şey olduğuna dair iyi bir açıklaması var.


7
-1. friendkötü bir şey değildir; aksine, bundan çok daha iyidir internal. Nelson'ın açıklaması kötü ve yanlış.
Nawaz

4

Bu sınırlama için mazeret bırakmayın. arkadaş kötü, ama iç iyi mi? aynı şeydir, sadece o arkadaş size kimin erişebileceği ve kimin erişemeyeceği konusunda daha kesin kontrol sağlar.

Bu kapsülleme paradigmasını uygulamak mı? yani erişimci yöntemleri yazmak zorundasınız ve şimdi ne olacak? Herkesin (B sınıfı yöntemleri hariç) bu yöntemleri çağırmasını nasıl durduracaksınız? yapamazsınız, çünkü bunu "arkadaş" eksik olduğu için kontrol edemezsiniz.

Hiçbir programlama dili mükemmel değildir. C # gördüğüm en iyi dillerden biri, ancak eksik özellikler için aptalca mazeretler yapmak kimseye yardımcı olmuyor. C ++, kolay olay / delege sistemi, yansıma (+ otomatik de / serileştirme) ve foreach özledim, ama C # Ben operatör aşırı yükleme (evet, bana ihtiyacınız olmadığını söylemeye devam), varsayılan parametreler, bir const kaçınılmaz, çoklu kalıtım (evet, bana ihtiyacın olmadığını ve arayüzlerin yeterli bir yedek olduğunu söylemeye devam et) ve bir örneği bellekten silmeye karar verme yeteneği (hayır, eğer bir tinkerer)


C # 'da çoğu operatörü aşırı yükleyebilirsiniz. Sen atama operatörleri aşırı olamaz, ama diğer taraftan sen geçersiz kılabilirsiniz ||/ &&onlara kısa devreyi tutarken. Varsayılan parametreler C # 4'e eklenmiştir.
CodesInChaos

2

Net 3'ten beri InternalsVisibleToAttribute var ama ben sadece ünite testinin yükselişinden sonra montajları test etmek için eklediklerinden şüpheleniyorum. Kullanmak için başka birçok neden göremiyorum.

Montaj düzeyinde çalışır, ancak dahili olmayan iş yapar; yani, bir montajı dağıtmak istediğiniz ancak dağıtılmamış başka bir montajın ayrıcalıklı erişimine sahip olmasını istediğiniz yer burasıdır.

Haklı olarak, birisinin korumalı meclisinizin yanında taklit bir arkadaş yaratmasını önlemek için arkadaş montajının güçlü bir şekilde anahtarlanmasını gerektirirler.


2

"Arkadaş" anahtar kelime hakkında birçok akıllı yorumlar okudum ve ne yararlı bir şey olduğunu kabul ediyorum, ama ne "iç" anahtar kelime daha az yararlı olduğunu düşünüyorum, ve her ikisi de saf OO programlama için hala kötü.

Neyimiz var? ("arkadaş" hakkında söylüyorum "iç" hakkında da söylüyorum)

  • "arkadaş" kullanmak kodu oo için daha az saf yapar?
  • Evet;

  • "arkadaş" kullanmamak kodu daha iyi yapar?

  • Hayır, yine de sınıflar arasında bazı özel ilişkiler kurmamız gerekiyor ve bunu sadece güzel kapsüllememizi kırdığımızda yapabiliriz, bu yüzden de iyi değil, "arkadaş" kullanmaktan daha kötü olduğunu söyleyebilirim.

Friend kullanmak bazı yerel problemler yapar, kullanmamak kod kütüphanesi kullanıcıları için problem yaratır.

programlama dili için ortak iyi bir çözüm şöyle görüyorum:

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

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

Bu konu hakkında ne düşünüyorsun? Bence en yaygın ve saf nesne yönelimli çözüm. Seçtiğiniz herhangi bir yönteme istediğiniz sınıfa açabilirsiniz.


Ben bir OOP nöbetçi değilim, ama görünüşe göre herkesin arkadaşlarına ve dâhiliğe karşı tutumlarını çözüyor. Ama C # sözdiziminin uzantısını önlemek için bir öznitelik kullanmanızı öneririm: [PublicFor Bar] void addBar (Bar bar) {} ... Bunun mantıklı olduğundan emin değilim; Niteliklerin nasıl çalıştığını veya erişilebilirlikle uğraşabileceklerini bilmiyorum (başka birçok "sihirli" şey yapıyorlar).
Grault

2

Sadece "Nasıl" sorusuna cevap vereceğim.

Burada çok cevap var, ancak bu özelliğe ulaşmak için bir tür "tasarım deseni" önermek istiyorum. Aşağıdakileri içeren basit bir dil mekanizması kullanacağım:

  • Arayüzler
  • İç içe sınıf

Örneğin 2 ana sınıfımız var: Öğrenci ve Üniversite. Öğrenci sadece üniversitenin erişmesine izin verilen genel not ortalamasına sahiptir. İşte 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();
}

1

Ben IL # zamanında derleme JIT - bina C # derleme modeli ile bir şey olduğundan şüpheleniyorum. yani: C # jeneriklerinin temel olarak C ++ jeneriklerinden farklı olmasının nedeni.


Derleyicinin en azından bir montaj içindeki sınıflar arasında bunu kolayca destekleyebileceğini düşünüyorum. Sadece hedef sınıftaki arkadaş sınıfı için erişim kontrolü rahatlatıcı bir mesele. Dış meclislerdeki sınıflar arasındaki arkadaşın belki de CLR'de daha temel değişikliklere ihtiyacı olabilir.
Kül

1

özel tutabilir ve işlevleri çağırmak için yansıma kullanabilirsiniz. Özel bir işlevi test etmesini isterseniz test çerçevesi bunu yapabilir


Bir Silverlight uygulaması üzerinde çalışmadığınız sürece.
kaalus

1

Arkadaşımı düzenli olarak kullanıyordum ve bunun OOP ihlali veya herhangi bir tasarım kusurunun işareti olduğunu düşünmüyorum. En az kodla doğru sonlamanın en etkili yolunun olduğu birkaç yer vardır.

Somut bir örnek, başka bir yazılıma iletişim arabirimi sağlayan arabirim derlemeleri oluştururken gösterilebilir. Genellikle, protokolün ve akran özelliklerinin karmaşıklığını ele alan ve istemci uygulaması ile montaj arasında iletilerin ve bildirimlerin iletilmesini içeren nispeten basit bir bağlantı / okuma / yazma / ileri / bağlantı kesme modeli sağlayan birkaç ağır siklet sınıf vardır. Bu mesajların / bildirimlerin sınıflara sarılması gerekir. Niteliklerin genellikle yaratıcıları olduğu için protokol yazılımı tarafından manipüle edilmesi gerekir, ancak birçok şeyin dış dünyaya salt okunur kalması gerekir.

Protokol / "yaratıcı" sınıfının, oluşturulan tüm sınıflara samimi bir şekilde erişmesinin OOP ihlali olduğunu açıklamak saçmadır - yaratıcı sınıf, yükselen her veriyi biraz bitirmek zorunda kaldı. En önemli bulduğum şey, "OOP's Sake için OOP" modelinin yol açtığı tüm BS ekstra kod satırlarını en aza indirmektir. Ekstra spagetti sadece daha fazla hata yapar.

Kullanıcılar dahili anahtar kelimeyi özellik, özellik ve yöntem düzeyinde uygulayabileceğinizi biliyor mu? Sadece üst düzey sınıf bildirimi için değil (çoğu örnek bunu gösteriyor gibi görünüyor.)

Friend anahtar sözcüğünü kullanan bir C ++ sınıfınız varsa ve bunu bir C # sınıfında taklit etmek istiyorsanız: 1. C # sınıfını herkese açık olarak belirtin 2. C ++ ile korunan ve böylece arkadaşların erişimine açık olan tüm öznitelikleri / özellikleri / yöntemleri beyan edin dahili C # 3. tüm iç özniteliklere ve özelliklere genel erişim için salt okunur özellikler oluşturma

Arkadaşımla% 100 aynı olmadığına katılıyorum ve birim test, arkadaş gibi bir şeye olan ihtiyacın çok değerli bir örneğidir (protokol analizörü günlük kodu gibi). Ancak internal, maruz kalmak istediğiniz sınıflara maruz kalmayı sağlar ve [InternalVisibleTo ()] gerisini halleder - özellikle birim test için doğmuş gibi görünür.

Bildiğim kadarıyla "daha iyi olmak, çünkü hangi sınıfların erişime sahip olduğunu açıkça kontrol edebilirsiniz" - ilk etapta aynı derlemede şüpheli kötü sınıflar ne yapıyor? Montajlarınızı bölümlere ayırın!


1

Arkadaşlık arayüzleri ve uygulamaları ayırarak simüle edilebilir. Fikir şudur: " Somut bir örnek isteyin, ancak söz konusu örneğin inşaat erişimini kısıtlayın ".

Örneğin

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()Herkese açık olmasına rağmen, sadece Friendsınıf ona erişebilir, çünkü başka hiç kimse Friendsınıfın somut bir örneğini alamaz . Özel bir kurucuya sahipken, fabrikaNew() yöntemi bir arabirim döndürür.

Ayrıntılar için arayüzlere kodlama yapmaksızın ücretsiz olarak arkadaşlar ve dahili arayüz üyeleri makaleme bakın.


Ne yazık ki dostluk mümkün olan herhangi bir şekilde simüle edilemez. Bu soruya bakın: stackoverflow.com/questions/5921612/…
kaalus

1

Bazıları, arkadaş kullanarak işlerin kontrolden çıkabileceğini öne sürdü. Kabul ediyorum, ama bu onun yararlılığını azaltmaz. Arkadaşınızın OO paradigmasını tüm sınıf üyelerinizi herkese açık hale getirmekten daha fazla incittiğinden emin değilim. Elbette dil, tüm üyelerinizi herkese açık hale getirmenize izin verecektir, ancak bu tür tasarım desenlerinden kaçınan disiplinli bir programcıdır. Benzer şekilde, disiplinli bir programcı, arkadaşının mantıklı olduğu belirli durumlar için kullanımını ayıracaktır. Bazı durumlarda içsel açılımları çok fazla hissediyorum. Neden bir sınıf veya yöntemi derlemedeki her şeye maruz bırakalım?

Kendi temel sayfamı devralan bir ASP.NET sayfası var, bu da System.Web.UI.Page devralır. Bu sayfada, korumalı bir yöntemde uygulama için son kullanıcı hata bildirimini işleyen bazı kodlar var

ReportError("Uh Oh!");

Şimdi, sayfada bulunan bir kullanıcı denetimim var. Kullanıcı denetiminin sayfadaki hata raporlama yöntemlerini çağırabilmesini istiyorum.

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

ReportError yöntemi korunuyorsa bunu yapamazsınız. Dahili yapabilirim, ancak montajdaki herhangi bir koda maruz kalır. Sadece geçerli sayfanın bir parçası olan UI öğelerine (alt denetimler dahil) maruz kalmasını istiyorum. Daha spesifik olarak, temel kontrol sınıfımın aynı hata raporlama yöntemlerini tanımlamasını ve sadece temel sayfada yöntemleri çağırmasını istiyorum.

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

Arkadaş gibi bir şeyin dili daha az "OO" yapmadan, belki de nitelikler gibi yapmadan faydalı olabileceğini ve uygulanabileceğine inanıyorum, böylece sınıfların veya yöntemlerin belirli sınıflara veya yöntemlere arkadaş olabilmesi, geliştiricinin özel erişim. Belki de ... (sahte kod)

[Friend(B)]
class A {

    AMethod() { }

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

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

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

Benim önceki örneğimde belki aşağıdaki gibi bir şey var (biri anlambilim tartışabilir, ama ben sadece fikri karşılamaya çalışıyorum):

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

Gördüğüm gibi, arkadaş kavramının bunun için bir şeyleri herkese açık hale getirmekten veya üyelere erişmek için genel yöntemler veya mülkler oluşturmaktan daha fazla riski yoktur. Herhangi bir arkadaş, verilerin erişilebilirliğinde başka bir ayrıntı düzeyine izin veriyorsa ve bu erişilebilirliği dahili veya genel olarak genişletmek yerine daraltmanıza izin veriyorsa.


0

C ++ ile çalışıyorsanız ve arkadaşınızın anahtar kelimesini kullanarak kendiniz bulursanız, bir tasarım sorununa sahip olduğunuzun çok güçlü bir göstergesidir, çünkü neden bir sınıfın diğer sınıfın özel üyelerine erişmesi gerekiyor?


Katılıyorum. Asla arkadaş anahtar kelimesine ihtiyacım yok. Genellikle karmaşık bakım sorunlarını çözmek için kullanılır.
Edouard A.19

7
Operatör aşırı yükleme, kimse ??
Hepimiz Monica

3
kaç kez operatörün aşırı yüklenmesi için iyi bir durum buldunuz?
bashmohandes

8
Size 'tasarım sorunu' yorumunuzu tamamen çürüten çok basit bir dava vereceğim. Üst-Alt. Bir ebeveyn çocuklarının sahibidir. Ebeveynin 'Çocuklar' koleksiyonundaki bir çocuğun sahibi bu ebeveyne aittir. Ebeveyn mülkünün Çocuk üzerindeki pasörü sadece kimse tarafından ayarlanmamalıdır. Sadece çocuk ebeveynin Çocuk koleksiyonuna eklendiğinde ve sadece atanması gerekir. Herkese açıksa, herkes değiştirebilir. Dahili: montajdaki herkes. Özel? Hiç kimse. Setter'ı ebeveyn olmak için bir arkadaş yapmak, bu vakaları ortadan kaldırır ve yine de sınıflarınızı ayrı tutar, ancak sıkı bir şekilde ilişkilidir. Hurra !!
Mark A. Donohoe

2
Çoğu temel yönetici / yönetilen model gibi birçok durum vardır. SO hakkında başka bir soru daha var ve kimse bunu bir arkadaş olmadan nasıl yapacağını anlayamadı. İnsanları bir sınıfın "her zaman yeterli olacağını" düşündüren şeyden emin değilim. Bazen birden fazla sınıfın birlikte çalışması gerekir.
kaalus

0

bsd

Arkadaşların saf OOness'e zarar verdiği belirtildi. Hangi katılıyorum.

Ayrıca arkadaşların da kabul ettiğim kapsüllemeye yardımcı olduğu belirtildi.

Bence arkadaşlık OO metodolojisine eklenmeli ama C ++ 'da olduğu gibi değil. Arkadaşımın sınıfının erişebileceği bazı alanlara / yöntemlere sahip olmak istiyorum, ancak TÜM alanlara / yöntemlere erişmelerini istemiyorum. Gerçek hayatta olduğu gibi, arkadaşlarımın kişisel buzdolabıma erişmesine izin verirdim ama banka hesabıma erişmelerine izin vermezdim.

Aşağıdaki gibi uygulanabilir

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

Bu elbette bir derleyici uyarısı oluşturur ve zekayı incitir. Ama işi yapacak.

Bir yan not olarak, kendinden emin bir programcının test birimini özel üyelere erişmeden yapması gerektiğini düşünüyorum. bu kapsamın dışında, ancak TDD hakkında okumaya çalışın. Ancak, yine de bunu yapmak istiyorsanız (arkadaşlarınız gibi c ++ kullanmak)

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

böylece tüm kodunuzu UNIT_TESTING'i tanımlamaksızın ve birim testini yapmak istediğinizde dosyanın ilk satırına #define UNIT_TESTING eklersiniz (ve #IT UNIT_TESTING altında birim testini yapan tüm kodu yazarsınız). Bu dikkatle ele alınmalıdır.

Birim testinin arkadaşların kullanımı için kötü bir örnek olduğunu düşündüğüm için, arkadaşların neden iyi olabileceğini düşündüğüm bir örnek vereceğim. Bir kesme sisteminiz (sınıf) olduğunu varsayalım. Kullanımla birlikte kırma sistemi aşınır ve yenilenmesi gerekir. Şimdi, sadece lisanslı bir tamircinin düzeltmesini istiyorsunuz. Örneği daha az önemsiz yapmak için, tamircinin bunu düzeltmek için kişisel (özel) tornavidasını kullanacağını söyleyebilirim. Bu yüzden mekanik sınıf, breakSystem sınıfının arkadaşı olmalıdır.


2
Bu FriendClass parametresi erişim kontrolü işlevi görmez: c.MyMethod((FriendClass) null, 5.5, 3)herhangi bir sınıftan çağrı yapabilirsiniz .
Jesse McGrew

0

Arkadaşlık, bazı iç sınıflar olan "ajanlar" kullanılarak da simüle edilebilir. Aşağıdaki örneği düşünün:

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

Yalnızca statik üyelere erişim için kullanıldığında çok daha basit olabilir. Bu tür bir uygulamanın faydaları, tüm türlerin dostluk sınıflarının iç kapsamında beyan edilmesidir ve arayüzlerin aksine, statik üyelere erişilmesine izin verir.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.