Versteckte Funktionen von C #? [geschlossen]

1475

Dies kam mir in den Sinn, nachdem ich aus dieser Frage Folgendes gelernt hatte :

where T : struct

Wir, C # -Entwickler, kennen alle die Grundlagen von C #. Ich meine Deklarationen, Bedingungen, Schleifen, Operatoren usw.

Einige von uns beherrschten sogar die Dinge wie Generika , anonyme Typen , Lambdas , LINQ , ...

Aber was sind die verborgensten Funktionen oder Tricks von C #, die selbst C # -Fans, Süchtige und Experten kaum kennen?

Hier sind die bisher offenbarten Funktionen:


Schlüsselwörter

Attribute

Syntax

Sprachmerkmale

Visual Studio-Funktionen

Rahmen

Methoden und Eigenschaften

  • String.IsNullOrEmpty()Methode von KiwiBastard
  • List.ForEach()Methode von KiwiBastard
  • BeginInvoke(), EndInvoke()Methoden von Will Dean
  • Nullable<T>.HasValueund Nullable<T>.ValueEigenschaften von Rismo
  • GetValueOrDefaultMethode von John Sheehan

Tipps

  • Schöne Methode für Event-Handler von Andreas HR Nilsson
  • Vergleiche in Großbuchstaben von John
  • Zugriff auf anonyme Typen ohne Reflexion durch dp
  • Ein schneller Weg, um Sammlungseigenschaften von Will träge zu instanziieren
  • JavaScript-ähnliche anonyme Inline-Funktionen von Roosteronacid

Andere

Serhat Ozgel
quelle

Antworten:

752

Dies ist nicht C # an sich, aber ich habe niemanden gesehen, der wirklich so viel verwendet System.IO.Path.Combine(), wie er sollte. Tatsächlich ist die gesamte Path-Klasse wirklich nützlich, aber niemand benutzt sie!

Ich bin bereit zu wetten, dass jede Produktions-App den folgenden Code hat, obwohl dies nicht der Fall sein sollte:

string path = dir + "\\" + fileName;
ageektrapped
quelle
584

Lambdas und Typinferenz werden unterschätzt. Lambdas können mehrere Anweisungen haben und sie werden automatisch als kompatibles Delegatenobjekt (stellen Sie nur sicher, dass die Signatur übereinstimmt) wie in:

Console.CancelKeyPress +=
    (sender, e) => {
        Console.WriteLine("CTRL+C detected!\n");
        e.Cancel = true;
    };

Beachten Sie, dass ich keine habe new CancellationEventHandlerund keine Typen von senderund angeben emuss, die aus dem Ereignis abgeleitet werden können. Aus diesem Grund ist das Schreiben des Ganzen weniger umständlich, delegate (blah blah)sodass Sie auch Parametertypen angeben müssen.

Lambdas müssen nichts zurückgeben und die Typinferenz ist in einem solchen Kontext äußerst leistungsfähig.

Übrigens können Sie immer Lambdas zurückgeben, die Lambdas im Sinne der funktionalen Programmierung machen. Hier ist zum Beispiel ein Lambda, das ein Lambda erstellt, das ein Button.Click-Ereignis behandelt:

Func<int, int, EventHandler> makeHandler =
    (dx, dy) => (sender, e) => {
        var btn = (Button) sender;
        btn.Top += dy;
        btn.Left += dx;
    };

btnUp.Click += makeHandler(0, -1);
btnDown.Click += makeHandler(0, 1);
btnLeft.Click += makeHandler(-1, 0);
btnRight.Click += makeHandler(1, 0);

Beachten Sie die Verkettung: (dx, dy) => (sender, e) =>

Deshalb bin ich froh, die funktionale Programmierklasse besucht zu haben :-)

Abgesehen von den Zeigern in C denke ich, dass es die andere grundlegende Sache ist, die Sie lernen sollten :-)

Chakrit
quelle
528

Von Rick Strahl :

Sie können die ?? Operator, damit Sie eine Reihe von Nullvergleichen durchführen können.

string result = value1 ?? value2 ?? value3 ?? String.Empty;
jfs
quelle
454

Alias-Generika:

using ASimpleName = Dictionary<string, Dictionary<string, List<string>>>;

Es ermöglicht Ihnen ASimpleName, anstelle von zu verwenden Dictionary<string, Dictionary<string, List<string>>>.

Verwenden Sie es, wenn Sie an vielen Stellen dasselbe generische große, lange, komplexe Ding verwenden würden.

BlackTigerX
quelle
438

Von CLR über C # :

Beim Normalisieren von Zeichenfolgen wird dringend empfohlen, ToUpperInvariant anstelle von ToLowerInvariant zu verwenden, da Microsoft den Code für die Durchführung von Vergleichen in Großbuchstaben optimiert hat .

Ich erinnere mich, dass mein Kollege vor dem Vergleich einmal die Zeichenfolgen in Großbuchstaben geändert hat. Ich habe mich immer gefragt, warum er das tut, weil ich finde, dass es "natürlicher" ist, zuerst in Kleinbuchstaben umzuwandeln. Nachdem ich das Buch jetzt gelesen habe, weiß ich warum.

jfs
quelle
254
Wenn Sie "eine Zeichenfolge in Großbuchstaben konvertieren", erstellen Sie ein zweites temporäres Zeichenfolgenobjekt. Ich dachte, dass diese Art des Vergleichs nicht bevorzugt wurde, der beste Weg war: String.Equals (stringA, stringB, StringComparison.CurrentCultureIgnoreCase), der diesen Wegwerf-String überhaupt nicht erstellt.
Anthony
32
Welche Art von Optimierung können Sie beim Vergleichen von Großbuchstaben durchführen, die bei Kleinbuchstaben nicht möglich sind? Ich verstehe nicht, warum einer optimaler wäre als der andere.
Parappa
36
Das Konvertieren in Groß- und Kleinbuchstaben kann in bestimmten Kulturen auch falsches Verhalten verhindern. Beispiel: Auf Türkisch werden zwei Kleinbuchstaben auf denselben Großbuchstaben I abgebildet. Google "türkisch i" für weitere Details.
Neil
34
Ich habe versucht, ToUpperInvariant mit ToLowerInvariant zu vergleichen. Ich kann unter .NET 2.0 oder 3.5 keinen Unterschied in der Leistung feststellen. Mit Sicherheit nichts, was eine "sehr empfehlenswerte" Verwendung übereinander rechtfertigt.
Rasmus Faber
19
ToUpperInvariant wird bevorzugt, da alle Zeichen einen Roundtrip ausführen. Siehe msdn.microsoft.com/en-us/library/bb386042.aspx . Für Vergleiche schreiben Sie"a".Equals("A", StringComparison.OrdinalIgnoreCase)
SLaks
408

Mein Lieblingstrick ist die Verwendung des Null-Koaleszenz-Operators und der Klammern, um Sammlungen für mich automatisch zu instanziieren.

private IList<Foo> _foo;

public IList<Foo> ListOfFoo 
    { get { return _foo ?? (_foo = new List<Foo>()); } }
Will
quelle
23
Fällt es Ihnen nicht schwer zu lesen?
Riri
72
Es ist etwas schwer zu lesen für den Anfänger, unerfahren. Aber es ist kompakt und enthält einige Muster und Sprachfunktionen, die der Programmierer kennen und verstehen sollte. Während es zunächst schwierig ist, bietet es den Vorteil, ein Grund zum Lernen zu sein.
38
Eine faule Instanziierung ist etwas unangebracht, weil es eine schlechte Wahl des Mannes ist, nicht an Klasseninvarianten zu denken. Es gibt auch Probleme mit der Parallelität.
John Leidegren
17
Ich wurde immer darüber informiert, dass dies eine schlechte Praxis ist und dass das Aufrufen einer Immobilie nichts als solches bewirken sollte. Wenn Sie dort einen Satz hätten und den Wert auf null setzen würden, wäre dies für jemanden, der Ihre API verwendet, sehr seltsam.
Ian
8
@Joel, außer dass das Setzen von ListOfFoo auf null weder Teil des Klassenvertrags ist noch eine gute Praxis ist. Deshalb gibt es keinen Setter. Dies ist auch der Grund, warum ListOfFoo garantiert eine Sammlung zurückgibt und niemals eine Null. Es ist nur eine sehr merkwürdige Beschwerde, dass wenn Sie zwei schlechte Dinge tun (einen Setter machen und eine Sammlung auf null setzen), dies dazu führt, dass Ihre Erwartungen falsch sind. Ich würde auch nicht vorschlagen, Environment.Exit () im Getter auszuführen. Das hat aber auch nichts mit dieser Antwort zu tun.
315

Vermeiden Sie es, nach Null-Ereignishandlern zu suchen

Das Hinzufügen eines leeren Delegaten zu Ereignissen bei der Deklaration und das Unterdrücken der Notwendigkeit, das Ereignis immer auf Null zu prüfen, bevor es aufgerufen wird, ist fantastisch. Beispiel:

public delegate void MyClickHandler(object sender, string myValue);
public event MyClickHandler Click = delegate {}; // add empty delegate!

Lass dich das machen

public void DoSomething()
{
    Click(this, "foo");
}

An Stelle von

public void DoSomething()
{
    // Unnecessary!
    MyClickHandler click = Click;
    if (click != null) // Unnecessary! 
    {
        click(this, "foo");
    }
}

Bitte beachten Sie auch diese verwandte Diskussion und diesen Blog-Beitrag von Eric Lippert zu diesem Thema (und möglichen Nachteilen).

andnil
quelle
87
Ich glaube, ein Problem wird auftreten, wenn Sie sich auf diese Technik verlassen und dann die Klasse serialisieren müssen. Sie werden das Ereignis eliminieren und dann bei der Deserialisierung eine NullRefference erhalten. Man kann sich also einfach an die "alte Art" halten, Dinge zu tun. Es ist sicherer.
Sirrocco
16
Sie können Ihren Ereignishandler weiterhin auf null setzen, sodass Sie weiterhin eine Nullreferenz erhalten und weiterhin eine Racebedingung haben.
Robert Paulson
64
Ein schneller Profiltest zeigt, dass ein Dummy-abonnierter Ereignishandler ohne Nulltest ungefähr das Zweifache der Zeit eines nicht abonnierten Ereignishandlers mit Nulltest benötigt. Multicast-Ereignishandler ohne Nulltest benötigt etwa das 3,5-fache der Zeit eines Singlecast-Ereignishandlers mit Nulltest.
P Daddy
54
Dies vermeidet die Notwendigkeit einer Nullprüfung, indem nur immer ein Selbstabonnent vorhanden ist. Selbst als leeres Ereignis ist dies mit einem Overhead verbunden, den Sie nicht möchten. Wenn es keine Abonnenten gibt, möchten Sie das Ereignis überhaupt nicht auslösen. Feuern Sie nicht immer zuerst ein leeres Dummy-Ereignis ab. Ich würde diesen schlechten Code in Betracht ziehen.
Keith
56
Dies ist aus den Gründen in den obigen Kommentaren ein schrecklicher Vorschlag. Wenn Sie Ihren Code "sauber" aussehen lassen müssen, verwenden Sie eine Erweiterungsmethode, um nach Null zu suchen, und rufen Sie dann das Ereignis auf. Jemand mit Änderungsrechten sollte dieser Antwort auf jeden Fall die Nachteile hinzufügen.
Greg
305

Alles andere plus

1) implizite Generika (warum nur für Methoden und nicht für Klassen?)

void GenericMethod<T>( T input ) { ... }

//Infer type, so
GenericMethod<int>(23); //You don't need the <>.
GenericMethod(23);      //Is enough.

2) einfache Lambdas mit einem Parameter:

x => x.ToString() //simplify so many calls

3) anonyme Typen und Initialisierer:

//Duck-typed: works with any .Add method.
var colours = new Dictionary<string, string> {
    { "red", "#ff0000" },
    { "green", "#00ff00" },
    { "blue", "#0000ff" }
};

int[] arrayOfInt = { 1, 2, 3, 4, 5 };

Noch einer:

4) Auto-Eigenschaften können unterschiedliche Bereiche haben:

public int MyId { get; private set; }

Danke @pzycoman, dass du mich daran erinnert hast:

5) Namespace-Aliase (nicht, dass Sie diese besondere Unterscheidung wahrscheinlich benötigen):

using web = System.Web.UI.WebControls;
using win = System.Windows.Forms;

web::Control aWebControl = new web::Control();
win::Control aFormControl = new win::Control();
Keith
quelle
14
in # 3 können Sie Enumerable.Range (1,5)
Echostorm
18
Ich denke, Sie konnten Arrays mit int [] nums = {1,2,3} initialisieren. seit 1.0 :) braucht nicht einmal das "neue" Schlüsselwort
Lucas
13
auch Lambda ohne Parameter () => DoSomething ();
Pablo Retyk
5
Ich habe beide verwendet {get; interner Satz; } und bekomme; geschütztes Set; }, also ist dieses Muster konsistent.
Keith
7
@ Kirk Broadhurst - Sie haben Recht - new web.Control()würde auch in diesem Beispiel funktionieren. Die ::Syntax zwingt es, das Präfix als Namespace-Alias ​​zu behandeln, sodass eine Klasse aufgerufen werden kann webund die web::ControlSyntax weiterhin funktioniert, während die web.ControlSyntax nicht weiß, ob die Klasse oder der Namespace überprüft werden soll. Aus diesem Grund verwende ich immer ::Namespace-Aliase.
Keith
286

Ich kannte das Schlüsselwort "as" eine ganze Weile nicht.

MyClass myObject = (MyClass) obj;

vs.

MyClass myObject = obj as MyClass;

Die zweite Option gibt null zurück, wenn obj keine MyClass ist, anstatt eine Klassenumwandlungsausnahme auszulösen.

Mike Stone
quelle
42
Übertreibe es aber nicht. Viele Leute scheinen zu verwenden, weil sie die Syntax bevorzugen, obwohl sie die Semantik eines (ToType) x wollen.
Scott Langham
4
Ich glaube nicht, dass es eine bessere Leistung bietet. Hast du es profiliert? (Natürlich, wenn die Besetzung fehlschlägt ... aber wenn Sie die Besetzung (MyClass) verwenden, sind Fehler außergewöhnlich ... und äußerst selten (wenn sie überhaupt auftreten), sodass es keinen Unterschied macht.
Scott Langham
7
Dies ist nur dann performanter, wenn im Normalfall die Besetzung fehlschlägt. Andernfalls ist das Objekt mit direkter Umwandlung (Typ) schneller. Es dauert jedoch länger, bis eine direkte Umwandlung eine Ausnahme auslöst, als wenn null zurückgegeben wird.
Spence
15
Genau wie das Schlüsselwort "as" ... ist das Schlüsselwort "is" genauso nützlich.
dkpatt
28
Sie können es missbrauchen und später eine NullReferenceException haben, wenn Sie früher eine InvalidCastException hätten haben können.
Andrei Rînea
262

Zwei Dinge, die mir gefallen, sind automatische Eigenschaften, mit denen Sie Ihren Code noch weiter reduzieren können:

private string _name;
public string Name
{
    get
    {
        return _name;
    }
    set
    {
        _name = value;
    }
}

wird

public string Name { get; set;}

Auch Objektinitialisierer:

Employee emp = new Employee();
emp.Name = "John Smith";
emp.StartDate = DateTime.Now();

wird

Employee emp = new Employee {Name="John Smith", StartDate=DateTime.Now()}
Lomaxx
quelle
6
Sollte beachtet werden, dass automatische Eigenschaften nur eine C # 3.0-Funktion sind?
Jared Updike
21
Mit dem 3.0-Compiler wurden automatische Eigenschaften eingeführt. Da der Compiler jedoch so eingestellt werden kann, dass er 2.0-Code ausgibt, funktionieren sie einwandfrei. Versuchen Sie einfach nicht, 2.0-Code mit automatischen Eigenschaften in einem älteren Compiler zu kompilieren!
Joshua Shannon
74
Vielen Menschen ist nicht klar, dass get und set unterschiedliche Zugänglichkeiten haben können, z. B.: Public string Name {get; privates Set;}
Nader Shirazie
7
Das einzige Problem mit den automatischen Eigenschaften besteht darin, dass es anscheinend nicht möglich ist, einen Standardinitialisierungswert anzugeben.
Stein Åsmul
7
@ ANeves: Nein, ist es nicht. Von dieser Seite: Ein DefaultValueAttribute bewirkt nicht, dass ein Mitglied automatisch mit dem Wert des Attributs initialisiert wird. Sie müssen den Anfangswert in Ihrem Code festlegen. [DefaultValue]wird für den Designer verwendet, damit er weiß, ob eine Eigenschaft in Fettdruck angezeigt werden soll (dh nicht standardmäßig).
Roger Lipscombe
255

Das 'Standard'-Schlüsselwort in generischen Typen:

T t = default(T);

führt zu 'null', wenn T ein Referenztyp ist, und 0, wenn es ein int ist, false, wenn es ein Boolescher Wert ist, usw.

Eric Minkes
quelle
4
Plus: Typ? als Verknüpfung für Nullable <Typ>. default (int) == 0, aber default (int?) == null.
Sunside
221

Das @ weist den Compiler an, alle Escape-Zeichen in einer Zeichenfolge zu ignorieren.

Ich wollte dies nur klarstellen ... es sagt ihm nicht, dass er die Escape-Zeichen ignorieren soll, sondern dass der Compiler die Zeichenfolge als Literal interpretieren soll.

Wenn Sie haben

string s = @"cat
             dog
             fish"

Es wird tatsächlich als gedruckt (beachten Sie, dass es sogar das zum Einrücken verwendete Leerzeichen enthält):

cat
             dog
             fish
Lomaxx
quelle
Würde die Zeichenfolge nicht alle Leerzeichen enthalten, die Sie zum Einrücken verwendet haben?
Andy
18
Ja, es heißt wörtliche Zeichenfolge.
Joan Venge
4
Es wäre klarer, wenn die Ausgabe die Leerzeichen zeigen würde, die ebenfalls ausgedruckt würden. Im Moment scheint es, als würden die Zeichen für neue Zeilen gedruckt, Leerzeichen werden jedoch ignoriert.
Aleemb
Sehr nützlich, um regulären Ausdrücken und langen SQL-Abfragen zu entgehen.
Asche999
Es bildet auch {{auf {und }}zu }macht es nützlich für string.Format().
Ferruccio
220

Ich denke, eine der am wenigsten geschätzten und weniger bekannten Funktionen von C # (.NET 3.5) sind Ausdrucksbäume , insbesondere in Kombination mit Generics und Lambdas. Dies ist ein Ansatz zur API-Erstellung, den neuere Bibliotheken wie NInject und Moq verwenden.

Angenommen, ich möchte eine Methode bei einer API registrieren und diese API muss den Methodennamen abrufen

Angesichts dieser Klasse:

public class MyClass
{
     public void SomeMethod() { /* Do Something */ }
}

Früher war es sehr üblich, dass Entwickler dies mit Zeichenfolgen und Typen (oder etwas anderem, das weitgehend auf Zeichenfolgen basiert) tun:

RegisterMethod(typeof(MyClass), "SomeMethod");

Nun, das ist scheiße wegen des Mangels an starker Typisierung. Was ist, wenn ich "SomeMethod" umbenenne? Jetzt, in Version 3.5, kann ich dies jedoch auf eine stark typisierte Weise tun:

RegisterMethod<MyClass>(cl => cl.SomeMethod());

In dem die RegisterMethod-Klasse Folgendes verwendet Expression<Action<T>>:

void RegisterMethod<T>(Expression<Action<T>> action) where T : class
{
    var expression = (action.Body as MethodCallExpression);

    if (expression != null)
    {
        // TODO: Register method
        Console.WriteLine(expression.Method.Name);
    }
}

Dies ist ein wichtiger Grund, warum ich gerade in Lambdas und Expression Trees verliebt bin.

Jason Olson
quelle
3
Ich habe eine Reflection Utility-Klasse, die das gleiche mit FieldInfo, PropertyInfo usw. tut.
Olmo
Ja, das ist großartig. Ich konnte Methoden wie diese verwenden, um Code wie EditValue(someEmployee, e => e.FirstName);in meiner Geschäftslogik zu schreiben und automatisch die gesamte Installationslogik für ein ViewModel und eine View zu generieren, um diese Eigenschaft zu bearbeiten (also eine Beschriftung mit dem Text "Vorname" und eine TextBox mit eine Bindung, die den Setter der FirstName-Eigenschaft aufruft, wenn der Benutzer den Namen bearbeitet, und die Ansicht mit dem Getter aktualisiert). Dies scheint die Grundlage für die meisten neuen internen DSLs in C # zu sein.
Scott Whitlock
4
Ich denke, diese sind weniger bekannt als weniger verstanden.
Justin Morgan
Warum müssen Sie eine Methode registrieren? Ich habe das noch nie benutzt - wo und wann würde das benutzt werden?
MoonKnight
209

" Ausbeute " würde mir in den Sinn kommen. Einige der Attribute wie [DefaultValue ()] gehören ebenfalls zu meinen Favoriten.

Das Schlüsselwort " var " ist etwas bekannter, aber dass Sie es auch in .NET 2.0-Anwendungen verwenden können (solange Sie den .NET 3.5-Compiler verwenden und ihn auf die Ausgabe von 2.0-Code einstellen), scheint nicht sehr bekannt zu sein Gut.

Edit: kokos, danke für den Hinweis auf die ?? Betreiber, das ist in der Tat sehr nützlich. Da es etwas schwierig ist, danach zu googeln (da ?? einfach ignoriert wird), finden Sie hier die MSDN-Dokumentationsseite für diesen Operator: ?? Operator (C # -Referenz)

Michael Stum
quelle
14
Die Dokumentation des Standardwerts besagt, dass der Wert der Eigenschaft nicht wirklich festgelegt wird. Es ist nur ein Helfer für Visualisierer und Codegeneratoren.
Boris Callens
2
DefaultValue: In der Zwischenzeit wird es von einigen Bibliotheken verwendet. ASP.net MVC verwendet DefaultValue für Parameter einer Controller-Aktion (was für nicht nullfähige Typen sehr nützlich ist). Genau genommen ist dies natürlich ein Codegenerator, da der Wert nicht vom Compiler, sondern vom MVC-Code festgelegt wird.
Michael Stum
6
Der Name für die ?? Operator ist der Operator "Null Coalescing"
Armstrongest
Ausbeute ist mein Favorit, der Koaleszenzoperator ist allerdings da oben. Ich sehe nichts über CAS oder Assembly Signing, starke Namen, den GAC ... Ich nehme an, nur CAS ist C # ... aber so viele Entwickler haben keine Ahnung von Sicherheit.
BrainSlugs83
198

Ich finde, dass die meisten C # -Entwickler nichts über 'nullable'-Typen wissen. Grundsätzlich Grundelemente, die einen Nullwert haben können.

double? num1 = null; 
double num2 = num1 ?? -100;

Setzen Sie ein nullbares Double, num1 , auf null und setzen Sie dann ein reguläres Double, num2 , auf num1 oder -100, wenn num1 null war.

http://msdn.microsoft.com/en-us/library/1t3y8s4s(VS.80).aspx

eine weitere Sache über Nullable-Typ:

DateTime? tmp = new DateTime();
tmp = null;
return tmp.ToString();

es ist return String.Empty. Überprüfen Sie diesen Link für weitere Details

Brad Barker
quelle
1
DateTime kann nicht auf null gesetzt werden.
Jason Jackson
2
Also ist "int" nur C # syntaktischer Zucker für System.Int32? Es gibt tatsächlich eine Compiler-Unterstützung, die auf den Nullable-Typen basiert und es ihnen ermöglicht, sie beispielsweise auf Null zu setzen (was nicht allein mit generischen Strukturen möglich ist) und sie als zugrunde liegenden Typ zu boxen.
P Daddy
4
@P Daddy - ja, int ist syntaktischer Zucker für System.Int32. Sie sind vollständig austauschbar, genau wie int? === Int32? === Nullable <Int32> === Nullable <int>
cjk
6
@ck: Ja, int ist ein Alias ​​für System.Int32, als T? ist ein Alias ​​für Nullable <T>, aber es ist nicht nur syntaktischer Zucker. Es ist ein definierter Teil der Sprache. Die Aliase erleichtern nicht nur das Lesen des Codes, sondern passen auch besser zu unserem Modell. Nullable <Double> als doppelt ausdrücken? unterstreicht die Perspektive, dass der so enthaltene Wert ein Double ist (oder sein kann), nicht nur eine generische Struktur, die beim Typ Double instanziiert wurde. (Fortsetzung)
P Daddy
3
... Auf jeden Fall ging es bei dem Argument nicht um Aliase (das war wohl nur eine schlechte Analogie), sondern darum, dass nullbare Typen - mit der von Ihnen bevorzugten Syntax - tatsächlich ein Sprachmerkmal sind, nicht nur ein Produkt von Generika. Sie können nicht alle Funktionen von Nullables (Vergleich mit / Zuweisung von Null, Operatorweiterleitung, Boxing des zugrunde liegenden Typs oder Null, Kompatibilität mit Null-Koaleszenz und asOperatoren) allein mit Generika reproduzieren. Nullable <T> allein ist rudimentär und alles andere als herausragend, aber das Konzept der nullbaren Typen als Teil der Sprache ist umwerfend.
P Daddy
193

Hier sind einige interessante versteckte C # -Funktionen in Form von undokumentierten C # -Schlüsselwörtern:

__makeref

__reftype

__refvalue

__arglist

Dies sind undokumentierte C # -Schlüsselwörter (sogar Visual Studio erkennt sie!), Die für ein effizienteres Boxen / Unboxing vor Generika hinzugefügt wurden. Sie arbeiten in Abstimmung mit der System.TypedReference-Struktur.

Es gibt auch __arglist, die für Parameterlisten mit variabler Länge verwendet wird.

Eine Sache, über die die Leute nicht viel wissen, ist System.WeakReference - eine sehr nützliche Klasse, die ein Objekt verfolgt, es dem Garbage Collector jedoch ermöglicht, es zu sammeln.

Die nützlichste "versteckte" Funktion wäre das Schlüsselwort "return return". Es ist nicht wirklich versteckt, aber viele Leute wissen nichts darüber. Darauf baut LINQ auf; Es ermöglicht verzögert ausgeführte Abfragen, indem eine Zustandsmaschine unter der Haube erzeugt wird. Raymond Chen hat kürzlich über die internen Details berichtet .

Judah Himango
quelle
2
Weitere Details zu den undokumentierten Stichwörtern von Peter Bromberg . Ich verstehe immer noch nicht, ob es Gründe gibt, sie jemals zu benutzen.
HuBeZa
@ HuBeZa Mit dem Aufkommen von Generika gibt es nicht viele (irgendwelche?) Gute Gründe, __refType, __makeref und __refvalue zu verwenden. Diese wurden hauptsächlich verwendet, um das Boxen vor Generika in .NET 2 zu vermeiden.
Judah Gabriel Himango
Matt, mit der Einführung von Generika in .NET 2 gibt es wenig Grund, diese Schlüsselwörter zu verwenden, da ihr Zweck darin bestand, das Boxen beim Umgang mit Werttypen zu vermeiden. Siehe den Link von HuBeZa und siehe auch codeproject.com/Articles/38695/UnCommon-C-keywords-A-Look#ud
Judah Gabriel Himango
185

Gewerkschaften (die C ++ Shared Memory-Art) in reinem, sicherem C #

Ohne auf unsicheren Modus und Zeiger zurückzugreifen, können Klassenmitglieder Speicherplatz in einer Klasse / Struktur gemeinsam nutzen. Gegeben die folgende Klasse:

[StructLayout(LayoutKind.Explicit)]
public class A
{
    [FieldOffset(0)]
    public byte One;

    [FieldOffset(1)]
    public byte Two;

    [FieldOffset(2)]
    public byte Three;

    [FieldOffset(3)]
    public byte Four;

    [FieldOffset(0)]
    public int Int32;
}

Sie können die Werte der Bytefelder ändern, indem Sie das Int32-Feld bearbeiten und umgekehrt. Zum Beispiel dieses Programm:

    static void Main(string[] args)
    {
        A a = new A { Int32 = int.MaxValue };

        Console.WriteLine(a.Int32);
        Console.WriteLine("{0:X} {1:X} {2:X} {3:X}", a.One, a.Two, a.Three, a.Four);

        a.Four = 0;
        a.Three = 0;
        Console.WriteLine(a.Int32);
    }

Gibt dies aus:

2147483647
FF FF FF 7F
65535

Fügen Sie einfach mit System.Runtime.InteropServices hinzu.

ZeroBugBounce
quelle
7
@George, wirkt Wunder, wenn Sie mit Legacy-Apps über Sockets mithilfe von Gewerkschaftserklärungen kommunizieren.
Scottm
2
Es ist auch sinnvoll, int und float bei Offset 0 zu sagen. Dies ist das, was Sie benötigen, wenn Sie Gleitkommazahlen als Bitmasken bearbeiten möchten, was Sie manchmal möchten. Vor allem, wenn Sie neue Dinge über Gleitkommazahlen lernen möchten.
John Leidegren
2
Das ärgerliche daran ist, dass der Compiler Sie zwingen wird, ALLE Variablen in der init-Funktion festzulegen, wenn Sie diese Struktur verwenden möchten. Wenn Sie also haben: public A (int int32) {Int32 = int32; } Es wird "Feld 'Eins' muss vollständig zugewiesen sein, bevor die Kontrolle an den Anrufer zurückgegeben wird" ausgegeben, daher müssen Sie Eins = Zwei = Drei = Vier = 0 setzen. auch.
Manixrock
2
Dies hat seine Verwendung, hauptsächlich mit Binärdaten. Ich verwende eine "Pixel" -Struktur mit einem int32 @ 0 und vier Bytes für die vier Komponenten @ 0, 1, 2 und 3. Ideal für die schnelle und einfache Bearbeitung von Bilddaten.
Snarf
57
Warnung: Bei diesem Ansatz wird die Endianness nicht berücksichtigt. Das bedeutet, dass Ihr C # -Code nicht auf allen Computern gleich ausgeführt wird. Auf Little-Endian-CPUs (die zuerst das niedrigstwertige Byte speichern) wird das gezeigte Verhalten verwendet. Bei Big-Endian-CPUs werden die Bytes jedoch umgekehrt ausgegeben, wie Sie es erwartet haben. Beachten Sie, wie Sie dies im Produktionscode verwenden - Ihr Code ist möglicherweise nicht auf bestimmte mobile Geräte und andere Hardware portierbar und kann auf nicht offensichtliche Weise beschädigt werden (z. B. zwei Dateien, die angeblich dasselbe Format haben, jedoch tatsächlich in umgekehrter Reihenfolge der Bytes).
Ray Burns
176

Verwenden von @ für Variablennamen, bei denen es sich um Schlüsselwörter handelt.

var @object = new object();
var @string = "";
var @if = IpsoFacto(); 
Mark Cidade
quelle
38
Warum sollten Sie ein Schlüsselwort als Variablennamen verwenden? Mir scheint, dies würde den Code weniger lesbar und verschleiert machen.
Jon
41
Nun, der Grund dafür ist, dass CLI es für die Interoperabilität mit anderen Sprachen benötigt, die möglicherweise C # -Schlüsselwörter als Mitgliedsnamen verwenden
Mark Cidade,
69
Wenn Sie jemals die asp.net MVC HTML-Helfer verwenden und eine HTML-Klasse definieren wollten, werden Sie froh sein zu wissen, dass Sie @class verwenden können, damit es nicht als Klassenschlüsselwort erkannt wird
Boris Callens
31
Ideal für Erweiterungsmethoden. public static void DoSomething (diese Bar @this, string foo) {...}
Jonathan C Dickinson
45
@ Zihotki: Falsch. var a = 5; Console.WriteLine (@a);
Druckt
168

Wenn Sie Ihr Programm beenden möchten, ohne endgültige Blöcke oder Finalizer aufzurufen , verwenden Sie FailFast :

Environment.FailFast()
Jan Bannister
quelle
12
Beachten Sie, dass diese Methode auch einen Speicherauszug erstellt und eine Nachricht in das Windows-Fehlerprotokoll schreibt.
RickNZ
1
Es sollte beachtet werden, dass keine Finalizer oder App Domain Unload-Ereignisse aufgerufen werden, wenn diese Methode verwendet wird ...
AwkwardCoder
1
+1 für den Hinweis, aber dies ist nicht C #, sondern BCL von .NET.
Abel
System.Diagnostics.Process.GetCurrentProcess (). Kill () ist schneller
Tolgahan Albayrak
153

Rückgabe anonymer Typen von einer Methode und Zugriff auf Mitglieder ohne Reflexion.

// Useful? probably not.
private void foo()
{
    var user = AnonCast(GetUserTuple(), new { Name = default(string), Badges = default(int) });
    Console.WriteLine("Name: {0} Badges: {1}", user.Name, user.Badges);
}

object GetUserTuple()
{
    return new { Name = "dp", Badges = 5 };
}    

// Using the magic of Type Inference...
static T AnonCast<T>(object obj, T t)
{
   return (T) obj;
}
Denis Phillips
quelle
42
Das bringt dir wirklich nichts. Es ist tatsächlich gefährlich. Was ist, wenn GetUserTuple so geändert wird, dass mehrere Typen zurückgegeben werden? Die Besetzung schlägt zur Laufzeit fehl. Eines der großartigen Dinge an C # /. Net ist die Überprüfung der Kompilierungszeit. Es wäre viel besser, nur einen neuen Typ zu erstellen.
Jason Jackson
9
@ Jason Ich habe gesagt, es ist wahrscheinlich nicht nützlich, aber es ist überraschend (und ich dachte versteckt).
Denis Phillips
31
Dies ist zwar cool, scheint aber eine eher schlechte Wahl für das Design zu sein. Sie haben den anonymen Typ grundsätzlich an zwei Stellen definiert. An diesem Punkt deklarieren Sie einfach eine echte Struktur und verwenden sie direkt.
Paul Alexander
6
@ George: Eine solche Konvention würde als ... Struktur bezeichnet?
R. Martinho Fernandes
2
Dieser Trick heißt "Cast by Sample" und funktioniert nicht, wenn sich eine Methode, die einen anonymen Typ zurückgibt, in einer anderen Assembly befindet.
Desco
146

Hier ist eine nützliche für reguläre Ausdrücke und Dateipfade:

"c:\\program files\\oldway"
@"c:\program file\newway"

Das @ weist den Compiler an, alle Escape-Zeichen in einer Zeichenfolge zu ignorieren.

Patrick
quelle
27
Außerdem akzeptiert eine @ -Konstante darin Zeilenumbrüche. Perfekt, wenn Sie einem String ein mehrzeiliges Skript zuweisen.
Tor Haugen
11
Vergessen Sie nicht, auch einem Anführungszeichen zu entkommen, indem Sie sie mit anderen Worten verdoppeln. [Code] var Candy = @ "Ich mag" "rote" "Zuckerstangen."; [/ Code]
Dave
5
Ich neige dazu, Pfade mit Path.Combine zu bauen. Ich benutze definitiv das @ für Regex!
Dan McClain
6
@new ist auch eine Variable anstelle eines Schlüsselworts: @this, @int, @return, @interface ... so weiter :)
IAbstract
Dieser kommt nicht annähernd an: Aber was sind die verborgensten Funktionen oder Tricks von C #, die selbst C # -Fans, Süchtige, Experten kaum kennen?
publicgk
141

Mixins. Wenn Sie mehreren Klassen ein Feature hinzufügen möchten, aber nicht für alle eine Basisklasse verwenden können, muss jede Klasse eine Schnittstelle implementieren (ohne Mitglieder). Schreiben Sie dann eine Erweiterungsmethode für die Schnittstelle , dh

public static DeepCopy(this IPrototype p) { ... }

Natürlich wird etwas Klarheit geopfert. Aber es funktioniert!

Dmitri Nesteruk
quelle
4
Ja, ich denke, das ist die wahre Kraft der Erweiterungsmethoden. Sie ermöglichen grundsätzlich die Implementierung von Schnittstellenmethoden.
John Bubriski
Dies ist auch praktisch, wenn Sie NHibernate (oder Castle ActiveRecord) verwenden und Schnittstellen für Ihre Sammlungen verwenden müssen. Auf diese Weise können Sie den Erfassungsschnittstellen Verhalten verleihen.
Ryan Lundy
Auf diese Weise werden im Grunde alle LINQ-Methoden für den Datensatz implementiert.
WCWedin
Hier ist ein Link, der über das spricht, worauf ich oben Bezug genommen habe, unter Verwendung von Erweiterungsmethoden mit Erfassungsschnittstellen in NHibernate oder Castle ActiveRecord
Ryan Lundy
7
Wenn sie nur Erweiterungseigenschaften zulassen würden !!!! Ich hasste es, eine Erweiterungsmethode schreiben zu müssen, die eine schreibgeschützte Eigenschaft sein wollte ...
John Gibb
130

Ich bin mir nicht sicher, warum irgendjemand jemals Nullable <bool> verwenden möchte. :-)

Richtig , falsch, FileNotFound ?

Michael Stum
quelle
87
Wenn Sie erwarten, dass ein Benutzer eine Ja-Nein-Frage beantwortet, ist null angemessen, wenn die Frage nicht beantwortet wurde
Omar Kooheji,
22
Nullable-Typen sind praktisch für die Interaktion mit einer Datenbank, in der Tabellenspalten häufig nullbar sind.
Tuinstoel
11
Ja, nein, Maybee?
Dan Blair
21
Speichern Sie die Werte einer ThreeState CheckBox
Shimmy Weitzhandler
8
Wie in SQL: Ja / Nein / Unbekannt.
Erikkallen
116

Dieser ist nicht so sehr "versteckt", als dass er falsch benannt ist.

Viel Aufmerksamkeit wird den Algorithmen "Map", "Reduce" und "Filter" gewidmet. Was die meisten Leute nicht merken, ist, dass .NET 3.5 alle drei dieser Algorithmen hinzugefügt hat, aber es gab ihnen sehr SQL-ähnliche Namen, basierend auf der Tatsache, dass sie Teil von LINQ sind.

"map" => Select
Transformiert Daten von einem Formular in ein anderes

"reduzieren" => Aggregat
Aggregiert Werte zu einem einzigen Ergebnis

"filter" => Where
Filtert Daten basierend auf einem Kriterium

Die Möglichkeit, mit LINQ Inline-Arbeiten an Sammlungen durchzuführen, für die früher Iterationen und Bedingungen erforderlich waren, kann unglaublich wertvoll sein. Es lohnt sich zu lernen, wie alle LINQ-Erweiterungsmethoden dazu beitragen können, Ihren Code viel kompakter und wartbarer zu machen.

Brad Wilson
quelle
1
Select verhält sich auch wie die "Return" -Funktion in Monaden. Siehe stackoverflow.com/questions/9033/hidden-features-of-c#405088
Mark Cidade
1
Die Verwendung von "select" ist nur erforderlich, wenn Sie die SQLish-Syntax verwenden. Wenn Sie die Syntax der Erweiterungsmethode verwenden - someCollection.Where (item => item.Price> 5.99M) - ist die Verwendung von select-Anweisungen nicht erforderlich.
Brad Wilson
7
@Brad, das (wo) ist eine Filteroperation. Versuchen Sie eine
Kartenoperation
2
LINQ ist die große Sache, die meiner Meinung nach mit C # passiert ist: stackoverflow.com/questions/2398818/…
Leniel Maccaferri
1
Die kleinste Signatur von Aggregate ist die "Reduzieren" -Funktion, die mittlere Signatur von Aggregate ist die viel leistungsstärkere "Falten" -Funktion!
Brett Widmeier
115
Environment.NewLine

für systemunabhängige Zeilenumbrüche.

nt.
quelle
10
Das nervige an diesem ist, dass es nicht im kompakten Framework enthalten ist.
Stormenet
14
Es ist erwähnenswert, dass dies spezifisch für die Hostplattform der Anwendung ist. Wenn Sie also Daten erstellen, die für ein anderes System bestimmt sind, sollten Sie \ n oder \ r \ n entsprechend verwenden.
Mesh
Dies ist Teil der BCL von .NET und keine Funktion von C # an sich.
Abel
111

Wenn Sie versuchen, geschweifte Klammern in einem String.Format-Ausdruck zu verwenden ...

int foo = 3;
string bar = "blind mice";
String.Format("{{I am in brackets!}} {0} {1}", foo, bar);
//Outputs "{I am in brackets!} 3 blind mice"
Portman
quelle
19
@Kyralessa: Eigentlich ja, es sind Zahnspangen, aber "geschweifte Klammern" sind ein alternativer Name für sie. [und ]sind eckige Klammern und <und >sind spitze Klammern. Siehe en.wikipedia.org/wiki/Bracket .
icktoofay
4
Du hast Recht. Aber als ich kommentierte, stand nicht "geschweifte" Klammern.
Ryan Lundy
2
Sehr schön darauf hinzuweisen. Denken Sie daran, dass mein erstes String.Format wie folgt aussah: String.Format ("{0} Ich bin in geschweiften Klammern {1} {2} {3}", "{", "}", 3, "blinde Mäuse"); Als ich herausfand, dass es mit {{und}} möglich ist, diesen zu entkommen, war ich so glücklich :)
Gertjan
Muahahahaa ... Programmierung bereits 3 Jahre in .Net und ich kannten diese nicht. : D
Arnis Lapsa
"lockige" Klammern, die sich ähnlich wie Escape-Sequenzen verhalten?
Pratik
104
  1. ??- Koaleszenzbetreiber
  2. using ( Anweisung / Direktive ) - ein großartiges Schlüsselwort, das nicht nur zum Aufrufen von Dispose verwendet werden kann
  3. schreibgeschützt - sollte mehr verwendet werden
  4. Netzmodule - schade, dass Visual Studio keine Unterstützung bietet
Doctor Jones
quelle
6
using kann auch verwendet werden, um einen langen Namespace auf eine bequemere Zeichenfolge zu aliasen, dh: using ZipEncode = MyCompany.UtilityCode.Compression.Zip.Encoding; Hier gibt es mehr: msdn.microsoft.com/en-us/library/sf0df423.aspx
Dave R.
2
Es ist wirklich nicht dasselbe. Wenn Sie Dispose aufrufen, können Sie die using-Anweisung verwenden, wenn Sie beim Aliasing von Typen eine using-Direktive verwenden.
Øyvind Skaar
2
Nur für den Fall, dass Sie einen Namen für # 1 möchten (wie Sie es mit dem ternären Operator getan haben), ?? wird als Null-Koaleszenzoperator bezeichnet.
J. Steen
4
@ LucasAardvark: Wie J Steen erwähnte, wird es als Null-Koaleszenz-Operator bezeichnet. Suche danach!
Kokos
1
Suchen nach ?? Betreiber bei Google versuchen: google.com/search?q=c%23+%3F%3F+operator
backslash17
103

@Ed, ich bin etwas zurückhaltend, wenn es darum geht, dies zu veröffentlichen, da es kaum mehr als ein Trottel ist. Ich möchte jedoch darauf hinweisen, dass in Ihrem Codebeispiel:

MyClass c;
  if (obj is MyClass)
    c = obj as MyClass

Wenn Sie 'is' verwenden möchten, warum sollten Sie dann eine sichere Besetzung mit 'as' durchführen? Wenn Sie festgestellt haben, dass es sich bei obj tatsächlich um MyClass handelt, eine Besetzung nach Moorstandard:

c = (MyClass)obj

... wird niemals scheitern.

Ebenso könnte man einfach sagen:

MyClass c = obj as MyClass;
if(c != null)
{
   ...
}

Ich weiß nicht genug über die Innereien von .NET, um sicher zu sein, aber mein Instinkt sagt mir, dass dies maximal zwei Typ-Casts-Operationen auf maximal eine reduzieren würde. Es ist kaum wahrscheinlich, dass die Verarbeitungsbank so oder so kaputt geht. Ich persönlich denke, die letztere Form sieht auch sauberer aus.

Dogmang
quelle
16
Wenn die Umwandlung genau dem Typ entspricht (Umwandlung in "A", wenn das Objekt "A" ist, nicht davon abgeleitet), ist die gerade Umwandlung ~ 3x SCHNELLER als "als". Beim Casting eines abgeleiteten Typs (Casting auf "A", wenn das Objekt "B" ist, was von "A" abgeleitet ist) ist der Straight Cast ~ 0,1x langsamer als "as". "ist", dann "wie" ist einfach albern.
P Daddy
2
Nein, aber Sie könnten "if ((c = obj als MyClass)! = Null)" schreiben.
Dave Van den Eynde
10
isund macht askeine User Casts. Der obige Code fragt also den isOperator, ob obj von MyClass abgeleitet ist (oder ob eine implizite systemdefinierte Umwandlung vorliegt). Auch isnicht auf null. Diese beiden Randfälle können für Ihren Code wichtig sein. Zum Beispiel möchten Sie vielleicht schreiben: if( obj == null || obj is MyClass ) c = (MyClass)obj; Dies unterscheidet sich jedoch stark von: try { c = (MyClass)obj; } catch { }Da erstere keine benutzerdefinierten Konvertierungen durchführen, letztere jedoch. Ohne die nullPrüfung wird erstere auch nicht gesetzt, cwann objist null.
Adam Luter
2
In IL geht eine Besetzung zu einer CASTCLASS, eine Besetzung zu einer ISINST-Anweisung.
John Gibb
5
Ich habe einen Test dafür durchgeführt, Casting IEnumerable<int>to List<int>und Casting object( new object()) to IEnumerable<int>, um sicherzustellen, dass es keine Fehler gibt: Direktcast: 5.43ns, is-> as cast: 6.75ns, as cast: 5.69ns. Testen Sie dann ungültige Casts: Direktcast: 3125000ns, als Cast: 5.41ns. Fazit: Machen Sie sich keine Gedanken mehr über den 1% -Faktor und stellen Sie sicher, dass Sie verwenden, wenn der Cast ungültig ist. Ausnahmen (auch wenn sie behandelt werden) sind im Vergleich zum Casting SEHR langsam. Wir sprechen von einem Faktor 578000, der langsamer ist. Denken Sie daran, dass der letzte Teil, der Rest spielt keine Rolle (.Net FW 4.0, Release Build)
Aidiakapi
98

Vielleicht keine fortgeschrittene Technik, aber eine, die ich ständig sehe und die mich verrückt macht:

if (x == 1)
{
   x = 2;
}
else
{
   x = 3;
}

kann verdichtet werden zu:

x = (x==1) ? 2 : 3;
JasonS
quelle
10
Der ternäre Operator wurde in meiner Gruppe gesperrt. Einige Leute mögen es einfach nicht, obwohl ich nie verstanden habe warum.
Justin R.
13
Ich denke, weil sie nicht die gleichen Dinge sind, ein Operator zu sein, keine Aussage. Ich bevorzuge den ersten Ansatz selbst. Es ist konsistenter und leicht erweiterbar. Operatoren können keine Anweisungen enthalten.
Sobald Sie den Text
16
kann zu x ++ komprimiert werden; Ok es ist sinnlos ^^
François
5
@ Guillaume: Um alle Werte von x zu berücksichtigen: x = 2 + System.Math.Min (1, System.Math.Abs ​​(x-1));
mbeckisch
38
Es wird tatsächlich als "bedingter Operator" bezeichnet - es ist ein ternärer Operator, da drei Argumente erforderlich sind . en.wikipedia.org/wiki/Conditional_operator
Blorgbeard ist am