Είναι η εσωτερική λέξη-κλειδί C # μυρωδιά κώδικα;

Σε αυτήν την ανάρτηση, θα δείξω γιατί πιστεύω ότι η εσωτερική λέξη-κλειδί, όταν τοποθετείται στα μέλη της τάξης, είναι μια μυρωδιά κώδικα και προτείνει καλύτερες εναλλακτικές λύσεις.

Ποια είναι η εσωτερική λέξη-κλειδί;

Στο C # η εσωτερική λέξη-κλειδί μπορεί να χρησιμοποιηθεί σε μια τάξη ή στα μέλη της. Είναι ένας από τους τροποποιητές πρόσβασης C #. Οι εσωτερικοί τύποι ή τα μέλη είναι προσβάσιμα μόνο σε αρχεία στην ίδια διάταξη . (C # εσωτερική τεκμηρίωση λέξεων-κλειδιών).

Γιατί χρειαζόμαστε την εσωτερική λέξη-κλειδί;

«Μια κοινή χρήση της εσωτερικής πρόσβασης είναι στην ανάπτυξη που βασίζεται σε στοιχεία, διότι επιτρέπει σε μια ομάδα στοιχείων να συνεργάζεται ιδιωτικά χωρίς να εκτίθεται στον υπόλοιπο κώδικα της εφαρμογής . Για παράδειγμα, ένα πλαίσιο για τη δημιουργία γραφικών διεπαφών χρήστη θα μπορούσε να παρέχει κλάσεις ελέγχου και φόρμας που συνεργάζονται χρησιμοποιώντας τα μέλη με εσωτερική πρόσβαση. Δεδομένου ότι αυτά τα μέλη είναι εσωτερικά, δεν εκτίθενται σε κώδικα που χρησιμοποιεί το πλαίσιο. " (C # εσωτερική τεκμηρίωση λέξεων-κλειδιών)

Αυτές είναι οι περιπτώσεις χρήσης που είδα για τη χρήση της εσωτερικής λέξης-κλειδιού σε ένα μέλος της τάξης:

  • Καλέστε την ιδιωτική λειτουργία μιας τάξης μέσα στο ίδιο συγκρότημα.
  • Για να δοκιμάσετε μια ιδιωτική λειτουργία, μπορείτε να την επισημάνετε ως εσωτερική και να εκθέσετε το dll στο δοκιμαστικό DLL μέσω του InternalsVisibleTo.

Και οι δύο περιπτώσεις μπορούν να θεωρηθούν ως κωδικός μυρωδιάς, λέγοντας ότι αυτή η ιδιωτική λειτουργία πρέπει να είναι δημόσια.

Ας δούμε μερικά παραδείγματα

Εδώ είναι ένα απλό παράδειγμα. μια συνάρτηση σε μια τάξη θέλει να αποκτήσει πρόσβαση σε μια ιδιωτική λειτουργία μιας άλλης τάξης.

class A{ public void func1(){ func2(); } private void func2(){} } class B{ public void func(A a){ a.func2(); //Compilation error 'A.func2()' is inaccessible due to its protection level } }

Η λύση είναι απλή - απλά επισημάνετε το A :: func2 ως δημόσιο.

Ας δούμε λίγο πιο περίπλοκο παράδειγμα:

public class A{ public void func1(){} private void func2(B b){} } internal class B{ public void func3(A a){ a.func2(this); //Compilation error 'A.func2(B)' is inaccessible due to its protection level } }

Ποιο είναι το πρόβλημα? απλώς επισημάνετε το func2 ως δημόσιο όπως κάναμε πριν.

public class A{ public void func1(){ ... } public void func2(B b){ ...} // Compilation error: Inconsistent accessibility: parameter type 'B' is less accessible than method 'A.func2(B)' } internal class B{ public void func3(A a){ a.func2(this); } }

Αλλά δεν μπορούμε; Το Β είναι μια εσωτερική τάξη, οπότε δεν μπορεί να είναι μέρος της υπογραφής μιας δημόσιας λειτουργίας μιας δημόσιας τάξης.

Αυτές είναι οι λύσεις που βρήκα, ταξινομημένες με ευκολία:

  1. Επισημάνετε τη συνάρτηση με την εσωτερική λέξη-κλειδί
public class A{ public void func1(){ } internal void func2(B b){} } internal class B{ public void func3(A a){ a.func2(this); } }

2. Δημιουργήστε μια εσωτερική διεπαφή

internal interface IA2{ void func2(B b); } public class A:IA2{ public void func1(){ var b = new B(); b.func3(this); } void IA2.func2(B b){} //implement IA2 explicitly because func2 can't be public } internal class B{ public void func3(A a){ ((IA2)a).func2(this); //use interface instead of class to access func2 } }

3. Εξαγάγετε το A.func2 σε μια άλλη εσωτερική τάξη και χρησιμοποιήστε το αντί του A.func2.

internal class C{ public void func2(B b){ //extract A:func2 to here } } public class A{ public void func1(){} private void func2(B b){ new C().func2(b); } } internal class B{ public void func3(){ //a is no longer needed new C().func2(this); //use internal class instead of private function } }

4. Αποσυνδέστε τη λειτουργία από εσωτερικές τάξεις και δημοσιεύστε τη. Αυτό εξαρτάται πολύ από το τι κάνει η λειτουργία με τις εισόδους της. Η αποσύνδεση των εσωτερικών τάξεων μπορεί να είναι πολύ εύκολη, πολύ σκληρή και ακόμη και αδύνατη (χωρίς να καταστρέφεται ο σχεδιασμός).

Αλλά δεν έχουμε δημόσιες τάξεις που χρησιμοποιούμε διεπαφές

Ας δούμε ένα πιο πραγματικό παράδειγμα:

public interface IA{ void func1(); } internal class A : IA { public void func1(){} private void func2(B b){} } internal class B{ public void func3(IA a){ a.func2(this); //Compilation error IA' does not contain a definition for 'func2' and no extension method 'func2' accepting a first argument of type 'IA' could be found } }

Ας δούμε πώς οι προηγούμενες λύσεις προσαρμόζονται σε αυτό το παράδειγμα:

  1. Επισήμανση λειτουργίας με εσωτερικό. Αυτό σημαίνει ότι θα πρέπει να κάνετε μετάδοση στην τάξη για να καλέσετε τη συνάρτηση, οπότε αυτό θα λειτουργήσει μόνο εάν η κλάση Α είναι η μόνη που εφαρμόζει τη διεπαφή , πράγμα που σημαίνει ότι το IA δεν κοροϊδεύεται στις δοκιμές και δεν υπάρχει άλλη τάξη παραγωγής που εφαρμόζει το IA .
public interface IA{ void func1(); } internal class A : IA { public void func1(){} internal void func2(B b){} } internal class B{ public void func3(IA a){ ((A)a).func2(this); //cast to A in order to accses func2 } }

2. Δημιουργήστε μια εσωτερική διεπαφή που επεκτείνει τη δημόσια διεπαφή.

internal interface IExtendedA : IA{ void func2(B b); } public interface IA{ void func1(); } internal class A : IExtendedA { public void func1(){} public void func2(B b){} } internal class B{ public void func3(IExtendedA a){ a.func2(this); } }

3. Εξαγάγετε το A.func2 σε μια άλλη εσωτερική τάξη και χρησιμοποιήστε το αντί του A.func2.

4. Αποσυνδέστε τη λειτουργία από εσωτερικές τάξεις και προσθέστε τη στη δημόσια διεπαφή.

Μπορούμε να δούμε ότι η εσωτερική λέξη-κλειδί είναι η ευκολότερη λύση , αλλά υπάρχουν και άλλες λύσεις που χρησιμοποιούν τα παραδοσιακά δομικά στοιχεία του OOP: τάξεις και διεπαφές . Μπορούμε να δούμε ότι η 2η λύση - η προσθήκη μιας εσωτερικής διεπαφής δεν είναι πολύ πιο δύσκολη από την επισήμανση της λειτουργίας με την εσωτερική λέξη-κλειδί.

Γιατί να μην χρησιμοποιήσετε την εσωτερική λέξη-κλειδί;

Όπως έδειξα στα προηγούμενα παραδείγματα, η χρήση της εσωτερικής λέξης-κλειδιού είναι η ευκολότερη λύση . Αλλά θα έχετε μια δύσκολη στιγμή στο μέλλον εάν θα πρέπει:

  • Μετακίνηση της δημόσιας κλάσης Α σε άλλο DLL (καθώς η εσωτερική λέξη-κλειδί δεν θα ισχύει πλέον για το ίδιο dll)
  • Δημιουργήστε μια άλλη κλάση παραγωγής που εφαρμόζει το IA
  • Χλευάσουμε IA σε δοκιμές

Μπορεί να σκεφτείτε "Αλλά αυτή είναι μόνο μια γραμμή κώδικα, εγώ ή οποιοσδήποτε άλλος μπορεί να τον αλλάξει εύκολα αν χρειαστεί". Τώρα έχετε μια γραμμή κώδικα που μοιάζει με αυτό:

((MyClass)a).internalFunction

αλλά αν και άλλοι θα πρέπει να καλέσουν αυτήν τη συνάρτηση, αυτή η γραμμή θα επικολληθεί μέσα στο DLL.

Το συμπέρασμά μου

Νομίζω ότι η σήμανση ενός μέλους της τάξης με την εσωτερική λέξη-κλειδί είναι μια μυρωδιά κώδικα . Στα παραδείγματα που έδειξα παραπάνω είναι η ευκολότερη λύση, ΑΛΛΑ μπορεί να προκαλέσει προβλήματα στο μέλλον. Η δημιουργία μιας εσωτερικής διεπαφής είναι σχεδόν εξίσου εύκολη και πιο ρητή.

Σύγκριση με C ++

Η λέξη-κλειδί C ++ «φίλος» είναι παρόμοια με την εσωτερική λέξη-κλειδί C #. Επιτρέπει σε μια τάξη ή μια λειτουργία να έχει πρόσβαση σε ιδιωτικά μέλη μιας τάξης. Η διαφορά είναι ότι επιτρέπει την πρόσβαση σε συγκεκριμένη κλάση ή λειτουργία και όχι σε όλες τις κλάσεις στο ίδιο DLL. Κατά τη γνώμη μου, αυτή είναι μια καλύτερη λύση από την εσωτερική λέξη-κλειδί C #.

Περαιτέρω ανάγνωση

Πρακτικές χρήσεις για την "εσωτερική" λέξη-κλειδί στο C #

Γιατί το C # δεν παρέχει τη λέξη-κλειδί «φίλος» τύπου C ++;