Wie kapsle ich 'globale' Variablen in C #? /beste Übung

9

Was ist in C # die beste Vorgehensweise zum Einkapseln von Variablen, die ich in mehreren Methoden verwenden muss? Ist es in Ordnung, sie einfach über den beiden Methoden an der Spitze meiner Klasse zu deklarieren?

Auch wenn ich App-Einstellungen aus meiner Konfigurationsdatei verwende, sollte ich einen Getter verwenden? so was...

private string mySetting{ get { return WebConfigurationManager.AppSettings["mySetting"]; } }

Was ist die beste Vorgehensweise?

user1944367
quelle
Was wäre der Zweck eines Getters, außer eine zusätzliche (und wahrscheinlich unnötige) Indirektionsebene hinzuzufügen?
Robert Harvey
4
Ein Getter ist viel besser als mehrere Anrufe, WebConfigurationManager.AppSettingsweil es viel einfacher ist, später zu ändern
Daniel Little
@Lavinski: Sicher, wenn Sie denken, dass Sie den Datenspeicher später gegen einen anderen austauschen könnten. In der Praxis kommt dies selten vor, und die Wahrscheinlichkeit, dass dies bei AppSettings der Fall ist, scheint verschwindend gering zu sein.
Robert Harvey
10
Ein "Getter" hat den Vorteil, dass Intellisense funktioniert - und Sie haben die Schlüsselzeichenfolge "mySetting" (die vom Compiler nicht überprüft wird, wenn sie richtig geschrieben ist) nur an einer Stelle.
Doc Brown

Antworten:

5

Es ist nicht nur OK. Laut dem Buch Clean Code ist es tatsächlich eine sehr gute Praxis, und Onkel Bob ermutigt sie wirklich. Eine von vielen Methoden verwendete Variable könnte einen hohen Grad an Kohäsion zwischen den Methoden aufweisen. Darüber hinaus könnte ein hoher Grad an Objektvariablen auch darauf hindeuten, dass diese Klasse in zwei Teile geteilt werden sollte, sodass die Deklaration als Objektvariablen Ihnen helfen könnte, versteckte Klassenkandidaten zu finden.

Variablen auf Objektebene sind keine globalen Variablen. Haben Sie also keine Angst, sie zu verwenden, wenn sie von verschiedenen Methoden gemeinsam genutzt werden sollen.

Uri
quelle
Vielen Dank für Ihre Hilfe, obwohl ich denke, als Sie Zusammenhalt sagten, meinten Sie wirklich Kopplung.
user1944367
Nein, ich meinte Zusammenhalt. Auch im Software Engineering-Kurs fiel es mir schwer, den Wunsch nach hoher Kohäsion zu verstehen. Normalerweise sehnen wir uns nach geringer Kopplung und hohem Zusammenhalt. Kopplung ist eine physikalische Sache, die wir mit unseren eigenen Methoden sehen können. Wenn eine Klasse eine andere Klasse verwendet, ist sie an diese gekoppelt. Wenn es tatsächlich instanziiert und Gegenstand dieser Klasse ist, dann ist es sehr paarweise. Zusammenhalt ist jedoch eher eine logische Sache. Hohe Kohäsion in einer Klasse bedeutet, dass ihre Methoden zu einer sehr ähnlichen Domäne gehören, auch wenn sie keine Variable zwischen ihnen gemeinsam haben.
Uri
Verschiedene Methoden, die eine Objektvariable verwenden, bedeuten nicht unbedingt, dass sie miteinander gekoppelt sind. Ich könnte eine Encrypter-Klasse mit einer char [] -Kennwortvariablen haben und Encrypt (String-Text) haben; und Entschlüsseln (Zeichenfolgentext); Methoden darin. Beide verwenden dieselbe Kennwortvariable, es besteht jedoch keine offensichtliche Kopplung zwischen ihnen. Möglicherweise stellen Sie jedoch fest, dass sie sich mit derselben Domäne befassen, dh mit der Textverschlüsselung. Soweit ich weiß, haben sie einen hohen Grad an Zusammenhalt, obwohl diese Klasse in zwei Teile geteilt werden könnte. Man könnte argumentieren, dass die Verschlüsselung nicht zum Bereich der Entschlüsselung gehört.
Uri
4

Es ist eine großartige Idee, Ihre Einstellungen konstant zu kapseln.

Ich erstelle eine Einstellungsklasse, entweder eine statische globale eine oder mehrere Instanzklassen, die ich dann mit der Abhängigkeitsinjektion verwalten werde. Dann lade ich beim Start alle Einstellungen aus der Konfiguration in diese Klasse.

Ich habe auch eine kleine Bibliothek geschrieben, die Reflexion nutzt, um dies noch einfacher zu machen.

Sobald meine Einstellungen in meiner Konfigurationsdatei sind

<?xml version="1.0" encoding="utf-8" ?>
<configuration>   
    <appSettings>
        <add key="Domain" value="example.com" />
        <add key="PagingSize" value="30" />
        <add key="Invalid.C#.Identifier" value="test" />
    </appSettings>
</configuration>

Ich erstelle je nach Bedarf eine statische oder Instanzklasse. Für einfache Anwendungen mit nur wenigen Einstellungen ist eine statische Klasse in Ordnung.

private static class Settings
{
    public string Domain { get; set; }

    public int PagingSize { get; set; }

    [Named("Invalid.C#.Identifier")]
    public string ICID { get; set; }

}

Dann benutze ich entweder meinen Bibliotheksaufruf Inflate.Staticoder Inflate.Instanceund das Coole ist, dass ich jede Schlüsselwertquelle verwenden kann.

using Fire.Configuration;

Inflate.Static( typeof(Settings), x => ConfigurationManager.AppSettings[x] );

Der gesamte Code hierfür befindet sich in GitHub unter https://github.com/Enexure/Enexure.Fire.Configuration

Es gibt sogar ein Nuget-Paket:

PM> Install-Package Enexure.Fire.Configuration

Code als Referenz:

using System;
using System.Linq;
using System.Reflection;
using Fire.Extensions;

namespace Fire.Configuration
{
    public static class Inflate
    {
        public static void Static( Type type, Func<string, string> dictionary )
        {
            Fill( null, type, dictionary );
        }

        public static void Instance( object instance, Func<string, string> dictionary )
        {
            Fill( instance, instance.GetType(), dictionary );
        }


        private static void Fill( object instance, Type type, Func<string, string> dictionary ) 
        {

            PropertyInfo[] properties;
            if (instance == null) {

                // Static
                properties = type.GetProperties( BindingFlags.Public | BindingFlags.Static | BindingFlags.DeclaredOnly );
            } else {

                // Instance
                properties = type.GetProperties( BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly );
            }

            // Get app settings and convert
            foreach (PropertyInfo property in properties) {
                var attributes = property.GetCustomAttributes( true );
                if (!attributes.Any( x => x is Ignore )) {

                    var named = attributes.FirstOrDefault( x => x is Named ) as Named;

                    var value = dictionary((named != null)? named.Name : property.Name);

                    object result;
                    if (ExtendConversion.ConvertTo(value, property.PropertyType, out result)) {
                        property.SetValue( instance, result, null );
                    }
                }
            }
        }
    }
}
Daniel Little
quelle