Unterstützt C # die Verwendung statischer lokaler Variablen?

Verwandte Themen: Wie erstelle ich eine statische lokale Variable in Java?


Verzeihen Sie, wenn dies ein Duplikat ist; Ich war mir ziemlich sicher, dass das vorher gefragt worden wäre, und ich schaute, fand aber keinen Dupe.

Kann ich eine statische lokale Variable in C # erstellen? Wenn das so ist, wie?

Ich habe eine statische private Methode, die selten verwendet wird. Die statische Methode verwendet einen regulären Ausdruck, den ich einmal und nur bei Bedarf initialisieren möchte.

In C könnte ich dies mit einer lokalen statischen Variable tun. Kann ich das in C # machen?

Wenn ich versuche, diesen Code zu kompilieren:

private static string AppendCopyToFileName(string f) { static System.Text.RegularExpressions.Regex re = new System.Text.RegularExpressions.Regex("\\(copy (\\d+)\\)$"); } 

… es gibt mir einen Fehler:

Fehler CS0106: Der Modifikator ‘static’ ist für dieses Element nicht gültig


Wenn keine lokale statische Variable vorhanden ist, könnte ich mir annähern, was ich möchte, indem ich eine kleine neue private statische class erstellt und sowohl die Methode als auch die Variable (Feld) in die class einfügt. So was:

 public class MyClass { ... private static class Helper { private static readonly System.Text.RegularExpressions.Regex re = new System.Text.RegularExpressions.Regex("\\(copy (\\d+)\\)$"); internal static string AppendCopyToFileName(string f) { // use re here... } } // example of using the helper private static void Foo() { if (File.Exists(name)) { // helper gets JIT'd first time through this code string newName = Helper.AppendCopyToFileName(name); } } ... } 

Wenn Sie mehr darüber nachdenken, würde die Verwendung einer solchen Helfer-class zu einer größeren Netto-Effizienzersparnis führen, da die Helper-class nicht JIT-fähig wäre oder geladen würde, wenn dies nicht notwendig wäre. Recht?

Nein, C # unterstützt dies nicht. Sie können nahe kommen mit:

 private static System.Text.RegularExpressions.Regex re = new System.Text.RegularExpressions.Regex("\\(copy (\\d+)\\)$"); private static string AppendCopyToFileName(string f) { } 

Der einzige Unterschied hier ist die Sichtbarkeit von ‘re’. Es ist der class nicht nur der Methode ausgesetzt.

Die Variable re wird initialisiert, wenn die enthaltende class zum ersten Mal verwendet wird. Behalten Sie dies in einer spezialisierten kleinen class.

Unglücklicherweise nicht. Ich habe diese Möglichkeit in C. wirklich geliebt.

Ich habe eine Idee, was Sie tun könnten.

Erstellen Sie eine class, die Zugriff auf instanzspezifische Werte bietet, die statisch beibehalten werden.

Etwas wie das:

 class MyStaticInt { // Static storage private static Dictionary  staticData = new Dictionary  (); private string InstanceId { get { StackTrace st = new StackTrace (); StackFrame sf = st.GetFrame (2); MethodBase mb = sf.GetMethod (); return mb.DeclaringType.ToString () + "." + mb.Name; } } public int StaticValue { get { return staticData[InstanceId]; } set { staticData[InstanceId] = value; } } public MyStaticInt (int initializationValue) { if (!staticData.ContainsKey (InstanceId)) staticData.Add (InstanceId, initializationValue); } } 

Kann so verwendet werden …

 class Program { static void Main (string[] args) { // Only one static variable is possible per Namespace.Class.Method scope MyStaticInt localStaticInt = new MyStaticInt (0); // Working with it localStaticInt.StaticValue = 5; int test = localStaticInt.StaticValue; } } 

Es ist keine perfekte Lösung, aber ein interessantes Spielzeug.

Pro Namespace.Class.Method-Bereich kann nur eine statische Variable dieses Typs vorhanden sein. functioniert nicht in Eigenschaftsmethoden – sie werden alle in denselben Namen aufgetriggers – get_InstanceId.

Warum erstellen Sie nicht ein static readonly in Ihrer class und initialisieren es möglicherweise in einem statischen Konstruktor?

Dadurch haben Sie den gleichen performancesvorteil – es wird nur einmal initialisiert.

Nicht in C #, nur in Visual Basic .NET:

 Sub DoSomething() Static obj As Object If obj Is Nothing Then obj = New Object Console.WriteLine(obj.ToString()) End Sub 

VB.NET hat viele schöne Dinge, die C # nicht hat, deshalb wähle ich VB.NET.

Was ist damit, da Sie möchten, dass es nur initialisiert wird, wenn es verwendet wird:

 private static System.Text.RegularExpressions.Regex myReg = null; public static void myMethod() { if (myReg == null) myReg = new Regex("\\(copy (\\d+)\\)$"); } 

C # unterstützt keine statischen lokalen Variablen. Zusätzlich zu dem, was oben veröffentlicht wurde, gibt es einen Link zu einem MSDN-Blogeintrag zum Thema:
http://blogs.msdn.com/b/csharpfaq/archive/2004/05/11/why-doesn-tc-support-static-method-variables.aspx

Im Sinne von Henks und BarretJs Antwort denke ich, dass Sie die Initialisierungskosten vermeiden und durch die Verwendung einer Eigenschaft noch näher kommen können.

 private Regex myReg = null; private Regex MyReg { get { if (myReg == null) myReg = new Regex("\\(copy (\\d+)\\)$"); return myReg; } } 

Dann verwenden Sie einfach MyReg (das Großbuchstabe ‘M’ in MyReg) überall in Ihrem Code. Das Schöne an dieser Lösung ist, dass (obwohl der Getter ein functionsaufruf unter der Haube ist) die Semantik der Eigenschaften bedeutet, dass Sie Code schreiben können, als ob MyReg eine Variable wäre.

Das obige ist, wie ich “Laufzeitkonstanten” einrichte, die eine einmalige Initialisierung zur Laufzeit erfordern.

Ich mache das gleiche auch mit nullfähigen Typen. Zum Beispiel,

 private bool? _BoolVar = null; private bool BoolVar { get { if (_BoolVar.HasValue) return (bool)_BoolVar; _BoolVar = /* your initialization code goes here */; return (bool)_BoolVar; } } 

Dann verwenden Sie BoolVar wie einen normalen normalen Bool in Ihrem Code. Ich verwende kein internes _BoolVar (den Sicherungsspeicher für die BoolVar-Eigenschaft), weil ich es einfach nicht brauche. Denken Sie daran, dies ist wie eine Laufzeitkonstante, daher gibt es keinen Setter. Wenn ich jedoch aus irgendeinem Grund den Wert der Laufzeitkonstante ändern musste, würde ich dies direkt für die nullfähige Variable _BoolVar tun.

Die Initialisierung könnte ziemlich kompliziert sein. Es wird jedoch nur einmal ausgeführt und nur beim ersten Zugriff auf die Immobilie. Sie haben die Wahl, die Neuinitialisierung des Laufzeitkonstantenwerts zu erzwingen, indem Sie _BoolVar auf null zurücksetzen.

Sicher. Sie müssen nur die private statische Variable außerhalb der Methode deklarieren.

  private static readonly System.Text.RegularExpressions.Regex re = new System.Text.RegularExpressions.Regex( "\\(copy (\\d+)\\)$" ); private static string AppendCopyToFileName( string f ) { //do stuff. } 

Dies ist praktisch das, was Sie tun, mit dem einzigen Unterschied, dass “re” die gesamte class im Gegensatz zu nur der Methode sichtbar macht.

Ich habe noch keine gute generische Lösung dafür gesehen, also dachte ich, ich hätte mir meine eigene ausgedacht. Ich sollte jedoch beachten, dass die Notwendigkeit (nicht immer) der Verwendung statischer lokaler Variablen wahrscheinlich ein Zeichen dafür ist, dass Sie Ihren Code aus den Gründen, die von vielen Leuten angegeben wurden, umgestalten sollten. state ist etwas für das Objekt, keine Methode. Mir gefällt jedoch die Idee, den scope von Variablen einzuschränken.

Ohne weiteres:

 public class StaticLocalVariable { private static Dictionary s_GlobalStates = new Dictionary(); private int m_StateKey; public StaticLocalVariable() { Initialize(default(T)); } public StaticLocalVariable( T value ) { Initialize(value); } private void Initialize( T value ) { m_StateKey = new StackTrace(false).GetFrame(2).GetNativeOffset(); if (!s_GlobalStates.ContainsKey(m_StateKey)) { s_GlobalStates.Add(m_StateKey, value); } } public T Value { set { s_GlobalStates[m_StateKey] = value; } get { return s_GlobalStates[m_StateKey]; } } } 

Dies ist natürlich nicht fadensicher, aber es würde nicht zu viel Arbeit kosten, um es zu schaffen. Es kann wie folgt verwendet werden:

 static void Main( string[] args ) { Console.WriteLine("First Call:"); Test(); Console.WriteLine(""); Console.WriteLine("Second Call:"); Test(); Console.ReadLine(); } public static void Test() { StaticLocalVariable intTest1 = new StaticLocalVariable(0); StaticLocalVariable intTest2 = new StaticLocalVariable(1); StaticLocalVariable doubleTest1 = new StaticLocalVariable(2.1); StaticLocalVariable doubleTest2 = new StaticLocalVariable(); Console.WriteLine("Values upon entering Method: "); Console.WriteLine(" intTest1 Value: " + intTest1.Value); Console.WriteLine(" intTest2 Value: " + intTest2.Value); Console.WriteLine(" doubleTest1 Value: " + doubleTest1.Value); Console.WriteLine(" doubleTest2 Value: " + doubleTest2.Value); ++intTest1.Value; intTest2.Value *= 3; doubleTest1.Value += 3.14; doubleTest2.Value += 4.5; Console.WriteLine("After messing with values: "); Console.WriteLine(" intTest1 Value: " + intTest1.Value); Console.WriteLine(" intTest1 Value: " + intTest2.Value); Console.WriteLine(" doubleTest1 Value: " + doubleTest1.Value); Console.WriteLine(" doubleTest2 Value: " + doubleTest2.Value); } // Output: // First Call: // Values upon entering Method: // intTest1 Value: 0 // intTest2 Value: 1 // doubleTest1 Value: 2.1 // doubleTest2 Value: 0 // After messing with values: // intTest1 Value: 1 // intTest1 Value: 3 // doubleTest1 Value: 5.24 // doubleTest2 Value: 4.5 // Second Call: // Values upon entering Method: // intTest1 Value: 1 // intTest2 Value: 3 // doubleTest1 Value: 5.24 // doubleTest2 Value: 4.5 // After messing with values: // intTest1 Value: 2 // intTest1 Value: 9 // doubleTest1 Value: 8.38 // doubleTest2 Value: 9 

Das Schachteln der verwandten Mitglieder in einer inneren class, wie Sie in Frage stellten, ist höchstwahrscheinlich das sauberste. Sie müssen Ihre übergeordnete Methode nicht in die innere class verschieben, wenn die statische Variable die Anruferinformationen abrufen kann.

 public class MyClass { ... class Helper { static Regex re = new Regex("\\(copy (\\d+)\\)$"); string caller; internal Helper([CallerMemberName] string caller = null) { this.caller = caller; } internal Regex Re { //can avoid hard coding get { return caller == "AppendCopyToFileName" ? re : null; } set { if (caller == "AppendCopyToFileName") re = value; } } } private static string AppendCopyToFileName(string f) { var re = new Helper().Re; //get new Helper().Re = ...; //set } private static void Foo() { var re = new Helper().Re; //gets null new Helper().Re = ...; //set makes no difference } } 
  1. Sie können die harte Kodierung von Methodennamen in der Eigenschaft mithilfe einiger Ausdrucksbaum-Tricks vermeiden.

  2. Sie können den Helper-Konstruktor vermeiden und die Eigenschaft als statisch StackTrace , aber Sie müssen die Anruferinformationen über StackTrace in der Eigenschaft StackTrace .

Schließlich ist in einer Methode immer const möglich, aber dann ist eine Variable nicht variabel, zwei, nur Zeitkonstanten zum Kompilieren sind zulässig. Ich muss nur sagen.

Drei Jahre später…

Sie können es mit einer erfassten lokalen Variablen approximieren.

  class MyNose { private static void Main() { var myNose= new MyNose(); var nosePicker = myNose.CreatePicker(); var x = nosePicker(); var y = nosePicker(); var z = nosePicker(); } public Func CreatePicker() { int boog = 0; return () => boog++; } } 

Ich habe eine statische class entwickelt, die dieses Problem auf relativ einfache Weise behandelt:

 using System.Collections.Generic; using System.Runtime.CompilerServices; public static class StaticLocal { static StaticLocal() { dictionary = new Dictionary>(); } public class Access { public T Value { get; set; } public Access(T value) { Value = value; } } public static Access Init(T value, [CallerFilePath]string callingFile = "", [CallerMemberName]string callingMethod = "", [CallerLineNumber]int lineNumber = -1) { var secondKey = callingFile + '.' + callingMethod; if (!dictionary.ContainsKey(lineNumber)) dictionary.Add(lineNumber, new Dictionary()); if (!dictionary[lineNumber].ContainsKey(secondKey)) dictionary[lineNumber].Add(secondKey, new Access(value)); return dictionary[lineNumber][secondKey]; } private static Dictionary> dictionary; } 

Es kann innerhalb einer Methode wie folgt implementiert werden:

 var myVar = StaticLocal.Init(1); Console.Writeline(++myVar.Value); 

Bei jedem nachfolgenden Aufruf der Methode ist der in myVar.Value enthaltene Wert der letzte Wert, auf den er eingestellt wurde, sodass wiederholte Aufrufe eine Folge von natürlichen Zahlen ausgeben. Die Init () – function setzt den Wert nur, wenn er nicht zuvor initialisiert wurde. Andernfalls wird nur ein Verweis auf ein Objekt zurückgegeben, das den Wert enthält.

Es verwendet die Attribute [CallerFilePath], [CallerMemberName] und [CallerLineNumber], um zu ermitteln, auf welches Element im Wörterbuch verwiesen wird. Dadurch werden Kollisionen zwischen Methoden mit demselben Namen oder Anrufen von denselben Leitungsnummern ausgeschlossen.

Ein paar Vorbehalte bezüglich seiner Verwendung:

  • Wie andere bereits festgestellt haben, lohnt es sich zu überlegen, ob Sie wirklich statische lokale Variablen verwenden müssen. Ihre Verwendung kann manchmal ein Zeichen dafür sein, dass Ihr Design errorshaft ist und möglicherweise Refactoring verwendet wird.
  • Diese Problembewältigungsmethode umfasst mehrere Ebenen der Indirektion, wodurch die Ausführung Ihres Programms verlangsamt wird. Es sollte nur verwendet werden, wenn es diese Kosten rechtfertigt.
  • Statische lokale Variablen können Ihnen dabei helfen, zu viele Member in Ihrer class zu deklarieren und sie so zu unterteilen, wo sie verwendet werden. Dies sollte gegen die Ausführungszeit abgewogen werden, kann sich jedoch manchmal lohnen. Auf der anderen Seite kann die Angabe von so vielen Mitgliedern innerhalb einer class ein Hinweis auf Designprobleme sein, die eine Überlegung wert sind.
  • Da diese Werte nach Abschluss der Ausführung ihrer Methoden weiterhin im Speicher verbleiben, müssen Sie bedenken, dass die Verwendung großer Speicherbereiche die Speicherbereinigung bis zum Abschluss des Programms verhindert, wodurch die verfügbaren Ressourcen verringert werden.

Dieser Ansatz ist für die meisten Fälle, in denen statische lokale Variablen verwendet werden sollen, möglicherweise zu viel. Die Verwendung der Indirektion für den Umgang mit separaten Dateien, Methoden und Zeilen ist für Ihr Projekt möglicherweise nicht erforderlich. In diesem Fall können Sie es vereinfachen, um Ihre Anforderungen zu erfüllen.