Ukryte funkcje C #? [Zamknięte]

1475

To przyszło mi do głowy po tym, jak nauczyłem się z tego pytania :

where T : struct

My, programiści C #, wszyscy znamy podstawy C #. Mam na myśli deklaracje, warunki, pętle, operatory itp.

Niektórzy z nas opanowali nawet takie rzeczy, jak Generics , anonimowe typy , lambda , LINQ , ...

Ale jakie są najbardziej ukryte funkcje lub sztuczki C #, o których nawet fani C #, uzależnieni, eksperci ledwo wiedzą?

Oto dotychczas ujawnione funkcje:


Słowa kluczowe

Atrybuty

Składnia

Funkcje językowe

Funkcje programu Visual Studio

Struktura

Metody i właściwości

Porady & Triki

  • Fajna metoda dla organizatorów wydarzeń autorstwa Andreasa HR Nilssona
  • Porównania wielkich liter Johna
  • Dostęp do typów anonimowych bez refleksji przez dp
  • Szybki sposób na leniwe tworzenie instancji właściwości kolekcji przez Willa
  • JavaScript-anonimowe funkcje wstawiane przez roosteronacid

Inny

Serhat Ozgel
źródło

Odpowiedzi:

752

To nie jest C # jako taki, ale nie widziałem nikogo, kto naprawdę używałby System.IO.Path.Combine()w takim stopniu, w jakim powinien. W rzeczywistości cała klasa Path jest naprawdę przydatna, ale nikt jej nie używa!

Jestem gotów się założyć, że każda aplikacja produkcyjna ma następujący kod, nawet jeśli nie powinna:

string path = dir + "\\" + fileName;
ageektrapped
źródło
584

lambdas i wnioskowanie o typie są niedoceniane. Lambdas może mieć wiele instrukcji i automatycznie podwajają się jako kompatybilny obiekt delegowany (po prostu upewnij się, że podpis jest zgodny), jak w:

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

Pamiętaj, że nie mam new CancellationEventHandlerani nie muszę określać typów senderi e, są one wywnioskowane na podstawie zdarzenia. Dlatego jest to mniej kłopotliwe w pisaniu całości, delegate (blah blah)która wymaga również określenia typów parametrów.

Lambda nie musi niczego zwracać, a wnioskowanie typu jest niezwykle potężne w takim kontekście.

A BTW, zawsze możesz zwrócić Lambdas, które sprawiają, że Lambdas w sensie programowania funkcjonalnego. Na przykład, oto lambda, która tworzy lambda, która obsługuje zdarzenie Button.Click:

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

Zwróć uwagę na łączenie: (dx, dy) => (sender, e) =>

Właśnie dlatego cieszę się, że wziąłem klasę programowania funkcjonalnego :-)

Poza wskazówkami w C myślę, że to kolejna podstawowa rzecz, której powinieneś się nauczyć :-)

czakrytu
źródło
528

Od Ricka Strahla :

Możesz połączyć? operator, dzięki czemu można wykonać kilka porównań zerowych.

string result = value1 ?? value2 ?? value3 ?? String.Empty;
jfs
źródło
454

Alias ​​generics:

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

Pozwala na użycie ASimpleNamezamiast Dictionary<string, Dictionary<string, List<string>>>.

Użyj go, gdy w wielu miejscach użyjesz tej samej ogólnej, długiej, skomplikowanej rzeczy.

BlackTigerX
źródło
438

Z CLR przez C # :

Podczas normalizacji ciągów zdecydowanie zaleca się stosowanie ToUpperInvariant zamiast ToLowerInvariant, ponieważ Microsoft zoptymalizował kod do wykonywania porównań wielkimi literami .

Pamiętam, jak kiedyś mój współpracownik zawsze zmieniał ciągi znaków na wielkie przed porównaniem. Zawsze zastanawiałem się, dlaczego to robi, ponieważ uważam, że bardziej „naturalne” jest przejście na małe litery. Po przeczytaniu książki wiem już dlaczego.

jfs
źródło
254
Kiedy „konwertujesz ciąg na wielkie litery”, tworzysz drugi tymczasowy obiekt ciągu. Pomyślałem, że tego rodzaju porównanie nie jest preferowane, a najlepszym sposobem jest: String.Equals (stringA, stringB, StringComparison.CurrentCultureIgnoreCase), który w ogóle nie tworzy tego niepotrzebnego ciągu.
Anthony
32
Jakiego rodzaju optymalizacji możesz dokonać, porównując ciągi wielkich liter, których nie można wykonać na ciągach małych liter? Nie rozumiem, dlaczego jedno byłoby bardziej optymalne od drugiego.
Parappa
36
Konwersja na wielkie litery zamiast na małe może również zapobiec nieprawidłowemu zachowaniu w niektórych kulturach. Na przykład w języku tureckim dwie małe litery i odwzorowują tę samą wielką literę I. Google „turecki i”, aby uzyskać więcej szczegółów.
Neil,
34
Próbowałem przeprowadzić testy porównawcze ToUpperInvariant vs ToLowerInvariant. Nie mogę znaleźć żadnej różnicy w ich wydajności w .NET 2.0 lub 3.5. Z pewnością nie ma nic, co uzasadniałoby „wysoce zalecane” używanie jednego nad drugim.
Rasmus Faber
19
ToUpperInvariant jest preferowany, ponieważ sprawia, że ​​wszystkie postacie są w obie strony. Zobacz msdn.microsoft.com/en-us/library/bb386042.aspx . Dla porównania napisz"a".Equals("A", StringComparison.OrdinalIgnoreCase)
SLaks
408

Moją ulubioną sztuczką jest użycie operatora koalescencji zerowej i nawiasów do automatycznego tworzenia instancji kolekcji.

private IList<Foo> _foo;

public IList<Foo> ListOfFoo 
    { get { return _foo ?? (_foo = new List<Foo>()); } }
woli
źródło
23
Czy nie jest ci trudno czytać?
Riri
72
Jest trochę trudny do odczytania dla ... niedoświadczonego. Ale jest kompaktowy i zawiera kilka wzorców i funkcji językowych, które programista powinien znać i rozumieć. Tak więc, choć na początku jest to trudne, zapewnia korzyść z bycia powodem do nauki.
38
Leniwa instancja jest nieco nadużyciem, ponieważ jest kiepskim wyborem, aby uniknąć myślenia o niezmiennikach klasowych. Ma również problemy z współbieżnością.
John Leidegren,
17
Zawsze informowano mnie, że jest to zła praktyka, że ​​dzwonienie do nieruchomości nie powinno robić czegoś takiego. Jeśli miałeś tam zestaw i ustawiłeś wartość na null, byłoby to bardzo dziwne dla kogoś, kto używa twojego API.
Ian
8
@Joel, z wyjątkiem tego, że ustawienie ListOfFoo na wartość null nie jest częścią umowy klasowej ani nie jest dobrą praktyką. Dlatego nie ma setera. Właśnie dlatego ListOfFoo gwarantuje zwrot kolekcji i nigdy nie ma wartości zerowej. To bardzo dziwna skarga, że ​​jeśli zrobisz dwie złe rzeczy (ustawiasz i ustawiasz wartość na zero), spowoduje to, że twoje oczekiwania będą błędne. Nie sugerowałbym też wykonywania Environment.Exit () w module pobierającym. Ale to również nie ma nic wspólnego z tą odpowiedzią.
315

Unikaj sprawdzania procedur obsługi zdarzeń o wartości NULL

Dodanie pustego delegata do zdarzeń w momencie deklaracji, eliminując potrzebę sprawdzania zdarzenia pod kątem wartości zerowej przed wywołaniem, jest niesamowite. Przykład:

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

Pozwól to zrobić

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

Zamiast tego

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

Zobacz także pokrewną dyskusję i ten post na blogu Erica Lipperta na ten temat (i możliwe wady).

andnil
źródło
87
Wierzę, że pojawi się problem, jeśli polegasz na tej technice, a następnie musisz serializować klasę. Wyeliminujesz to zdarzenie, a następnie po deserializacji otrzymasz NullRefference .... Tak więc można po prostu trzymać się „starego sposobu” robienia rzeczy. To jest bezpieczniejsze.
sirrocco
16
nadal możesz ustawić moduł obsługi zdarzeń na zero, więc nadal możesz otrzymać zerowe odniesienie i nadal masz warunki wyścigu.
Robert Paulson,
64
Szybki test profilu pokazuje, że zasubskrybowany program obsługi zdarzeń bez testu zerowego zajmuje około dwukrotnie więcej czasu niż subskrypcja procedury obsługi zdarzeń z testem zerowym. Moduł obsługi zdarzeń multiemisji bez testu zerowego zajmuje około 3,5 razy więcej czasu niż moduł obsługi zdarzeń pojedynczej emisji z testem zerowym.
P Daddy,
54
Pozwala to uniknąć konieczności sprawdzania wartości zerowej, po prostu zawsze mając abonenta. Nawet jako puste wydarzenie pociąga za sobą koszty, których nie chcesz. Jeśli nie ma subskrybentów, których nie chcesz w ogóle uruchamiać, nie zawsze najpierw odpal puste, fałszywe zdarzenie. Rozważałbym ten zły kod.
Keith,
56
Jest to okropna sugestia z powodów w powyższych komentarzach. Jeśli musisz sprawić, by kod wyglądał „czysto”, użyj metody rozszerzenia, aby sprawdzić, czy null, a następnie wywołaj zdarzenie. Ktoś z uprawnieniami do modyfikowania zdecydowanie powinien dodać minusy do tej odpowiedzi.
Greg
305

Wszystko inne plus

1) niejawne leki generyczne (dlaczego tylko na metodach, a nie na klasach?)

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

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

2) proste lambdy z jednym parametrem:

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

3) anonimowe typy i inicjatory:

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

Inny:

4) Właściwości automatyczne mogą mieć różne zakresy:

public int MyId { get; private set; }

Dzięki @pzycoman za przypomnienie:

5) Aliasy przestrzeni nazw (nie dlatego, że prawdopodobnie będziesz potrzebować tego konkretnego rozróżnienia):

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

web::Control aWebControl = new web::Control();
win::Control aFormControl = new win::Control();
Keith
źródło
14
w # 3 możesz zrobić Enumerable.Range (1,5)
Echostorm
18
myślę, że byłeś w stanie zainicjować tablice int [] nums = {1,2,3}; od 1.0 :) nawet nie potrzebuje słowa kluczowego „new”
Lucas
13
także lambda bez parametrów () => DoSomething ();
Pablo Retyk
5
Użyłem obu {get; zestaw wewnętrzny; } i dostać; zestaw chroniony; }, więc ten wzór jest spójny.
Keith
7
@Kirk Broadhurst - masz rację - new web.Control()również działałby w tym przykładzie. W ::Zmusza składniowe traktować jako alias prefiks przestrzeni nazw, więc można mieć klasę o nazwie webi web::Controlskładnia będzie nadal działać, a web.Controlskładnia nie wiem czy by sprawdzić klasę lub nazw. Z tego powodu zwykle używam ::przy tworzeniu aliasów przestrzeni nazw.
Keith
286

Od dłuższego czasu nie znałem słowa kluczowego „jako”.

MyClass myObject = (MyClass) obj;

vs

MyClass myObject = obj as MyClass;

Drugi zwróci null, jeśli obj nie jest MyClass, zamiast zgłosić wyjątek rzutowania na klasę.

Mike Stone
źródło
42
Nie przesadzaj jednak. Wydaje się, że wiele osób używa tego, ponieważ wolą składnię, chociaż chcą semantyki (ToType) x.
Scott Langham,
4
Nie wierzę, że oferuje lepszą wydajność. Czy profilowałeś to? (Oczywiście, że dzieje się tak, gdy rzutowanie nie powiedzie się ... ale kiedy używasz obsady (MyClass), niepowodzenia są wyjątkowe ... i niezwykle rzadkie (jeśli w ogóle się zdarzają), więc to nie ma znaczenia.
Scott Langham,
7
Jest to bardziej wydajne, jeśli zwykłym przypadkiem jest niepowodzenie rzutowania. W przeciwnym razie obiekt rzutowany bezpośrednio (typ) jest szybszy. Rzutowanie wyjątku zajmuje dłużej niż zwrócenie wartości null.
Spence
15
W tym samym wierszu słowa kluczowego „jak” ... słowo kluczowe „jest” jest równie przydatne.
dkpatt
28
Możesz nadużyć tego i mieć później NullReferenceException, gdy wcześniej mógłbyś mieć InvalidCastException.
Andrei Rînea
262

Dwie rzeczy, które lubię, to właściwości automatyczne, dzięki czemu możesz jeszcze bardziej zwinąć kod:

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

staje się

public string Name { get; set;}

Również obiekty inicjujące:

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

staje się

Employee emp = new Employee {Name="John Smith", StartDate=DateTime.Now()}
lomaxx
źródło
6
Czy należy zauważyć, że właściwości automatyczne są tylko funkcją C # 3.0?
Jared Updike,
21
Automatyczne właściwości zostały wprowadzone w kompilatorze 3.0. Ale ponieważ kompilator można ustawić na wyświetlanie kodu 2.0, działają one dobrze. Tylko nie próbuj kompilować kodu 2.0 z właściwościami automatycznymi w starszym kompilatorze!
Joshua Shannon
74
Wiele osób nie zdaje sobie sprawy z tego, że get i set mogą mieć różną dostępność, np .: public string Name {get; zestaw prywatny;}
Nader Shirazie
7
Jedynym problemem związanym z właściwościami automatycznymi jest to, że nie jest możliwe podanie domyślnej wartości inicjalizacji.
Stein Åsmul,
7
@ANeves: nie, to nie jest. Z tej strony: DefaultValueAttribute nie spowoduje, że członek zostanie automatycznie zainicjowany wartością atrybutu. Musisz ustawić wartość początkową w swoim kodzie. [DefaultValue]służy projektantowi, więc wie, czy wyświetlać pogrubioną właściwość (co oznacza, że ​​nie jest domyślna).
Roger Lipscombe
255

„Domyślne” słowo kluczowe w typach ogólnych:

T t = default(T);

skutkuje wartością „null”, jeśli T jest typem odniesienia, a 0, jeśli jest liczbą całkowitą, false, jeśli jest wartością logiczną itp.

Eric Minkes
źródło
4
Plus: typ? jako skrót dla Nullable <type>. default (int) == 0, ale default (int?) == null.
sunside
221

@ Mówi kompilatorowi, aby zignorował wszelkie znaki specjalne w ciągu.

Chciałem tylko wyjaśnić ten ... nie mówi, aby ignorował znaki specjalne, w rzeczywistości mówi kompilatorowi, aby interpretował ciąg jako literał.

Jeśli masz

string s = @"cat
             dog
             fish"

faktycznie wydrukuje jako (zwróć uwagę, że zawiera nawet spację używaną do wcięcia):

cat
             dog
             fish
lomaxx
źródło
czy ciąg nie zawiera wszystkich spacji użytych do wcięcia?
andy,
18
Tak, nazywa się to ciągiem dosłownym.
Joan Venge
4
Byłoby wyraźniej, gdyby dane wyjściowe pokazywały również spacje, które zostaną wydrukowane. W tej chwili wydaje się, że znaki nowych wierszy są drukowane, ale spacje są ignorowane.
aleemb
Bardzo przydatny do unikania wyrażeń regularnych i długich zapytań SQL.
ashes999
Mapuje również {{do {i }}na }co przydatne dla string.Format().
Ferruccio,
220

Myślę, że jedną z najbardziej niedocenianych i mniej znanych funkcji C # (.NET 3.5) są drzewa wyrażeń , szczególnie w połączeniu z Generics i Lambdas. Jest to podejście do tworzenia API, z którego korzystają nowsze biblioteki, takie jak NInject i Moq.

Powiedzmy na przykład, że chcę zarejestrować metodę za pomocą interfejsu API i że interfejs API musi uzyskać nazwę metody

Biorąc pod uwagę tę klasę:

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

Wcześniej bardzo często obserwowano, jak programiści robią to z łańcuchami i typami (lub czymś innym, w dużej mierze opartym na łańcuchach):

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

Cóż, to do bani z powodu braku silnego pisania. Co się stanie, jeśli zmienię nazwę „SomeMethod”? Teraz, w wersji 3.5, mogę to zrobić w mocno typowy sposób:

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

W którym klasa RegisterMethod używa w Expression<Action<T>>ten sposób:

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

Jest to jeden z głównych powodów, dla których jestem teraz zakochany w Lambdas i drzewkach ekspresyjnych.

Jason Olson
źródło
3
Mam klasę narzędzia do refleksji, która robi to samo z FieldInfo, PropertyInfo itp.
Olmo
Tak, to wspaniale. Byłem w stanie użyć takich metod do pisania kodu jak EditValue(someEmployee, e => e.FirstName);w mojej logice biznesowej i automatycznie wygenerowałem całą logikę hydrauliczną dla ViewModel i View do edycji tej właściwości (więc etykieta z tekstem „First Name” i TextBox z powiązanie, które wywołuje funkcję ustawiającą właściwość FirstName, gdy użytkownik edytuje nazwę i aktualizuje widok za pomocą modułu pobierającego). Wydaje się, że jest to podstawa dla większości nowych wewnętrznych DSL w C #.
Scott Whitlock,
4
Myślę, że nie są one tak mało znane, jak mniej zrozumiane.
Justin Morgan
Dlaczego musisz zarejestrować metodę? Nigdy wcześniej tego nie używałem - gdzie i kiedy miałoby to być wykorzystane?
MoonKnight
209

przychodzi mi na myśl „ dochód ”. Niektóre atrybuty, takie jak [DefaultValue ()], również należą do moich ulubionych.

Słowo kluczowe „ var ” jest nieco bardziej znane, ale można go również używać w aplikacjach .NET 2.0 (o ile używasz kompilatora .NET 3.5 i ustawiasz go na kod wyjściowy 2.0), wydaje się, że nie jest bardzo znany dobrze.

Edycja: kokos, dzięki za wskazanie ?? operator, to naprawdę bardzo przydatne. Ponieważ wyszukiwanie google jest trochę trudne (ponieważ ?? jest po prostu ignorowane), oto strona dokumentacji MSDN tego operatora: ?? Operator (C # Reference)

Michael Stum
źródło
14
Dokumentacja wartości domyślnej mówi, że tak naprawdę nie ustawia wartości właściwości. To tylko pomocnik dla wizualizatorów i generatorów kodu.
Boris Callens,
2
Co do DefaultValue: w międzyczasie niektóre biblioteki go używają. ASP.net MVC używa DefaultValue w parametrach akcji kontrolera (co jest bardzo przydatne w przypadku typów niedopuszczalnych). Ściśle mówiąc, jest to generator kodu, ponieważ wartość nie jest ustawiana przez kompilator, ale przez kod MVC.
Michael Stum
6
Nazwa dla ?? operator jest operatorem „Null Coalescing”
Armstrongest
wydajność jest moim ulubionym, ale operator koalescencji jest tam na górze. Nie widzę nic o CAS, podpisywaniu asemblera, mocnych nazwach, GAC ... Przypuszczam, że tylko CAS to C # ... ale tak wielu programistów nie ma pojęcia o bezpieczeństwie.
BrainSlugs83
198

Zwykle stwierdzam, że większość programistów C # nie wie o typach „zerowalnych”. Zasadniczo prymitywy, które mogą mieć wartość zerową.

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

Ustaw nullable double, num1 , na null, a następnie ustaw zwykłe double, num2 , na num1 lub -100, jeśli num1 było null.

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

jeszcze jedna rzecz o typie Nullable:

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

zwracany jest String.Empty. Sprawdź ten link, aby uzyskać więcej informacji

Brad Barker
źródło
1
DateTime nie może być ustawiony na null.
Jason Jackson
2
Zatem „int” to po prostu cukier syntaktyczny w języku C # dla System.Int32? W rzeczywistości istnieje obsługa kompilatora wokół typów Nullable, umożliwiając na przykład ustawienie wartości null (czego nie można zrobić przy użyciu samych struktur ogólnych) i umieszczanie ich w ramkach jako ich typ podstawowy.
P Daddy,
4
@P Daddy - tak, int to cukier składniowy dla System.Int32. Są całkowicie wymienne, podobnie jak int? === Int32? === Nullable <Int32> === Nullable <int>
cjk
6
@ck: Tak, int to alias dla System.Int32, as T? to pseudonim Nullable <T>, ale nie jest to tylko cukier syntaktyczny. To określona część języka. Aliasy nie tylko ułatwiają odczytanie kodu, ale także lepiej pasują do naszego modelu. Wyrażanie Nullable <Double> jako double? podkreśla perspektywę, że zawarta w ten sposób wartość jest (lub może być) podwójna, a nie tylko ogólna struktura utworzona na typie Double. (ciąg dalszy)
P Daddy
3
... W każdym razie spór nie dotyczył aliasów (to chyba tylko kiepska analogia), ale te typy zerowalne - przy użyciu dowolnej składni - są rzeczywiście cechą językową, a nie tylko produktem generycznym. Nie można odtworzyć całej funkcjonalności wartości zerowych (porównanie z / przypisanie wartości zerowej, przekazywanie operatora, boksowanie typu bazowego lub wartości zerowej, zgodność z koalescencją zerową i asoperatorami) tylko z danymi ogólnymi. Nullable <T> sam jest szczątkowy i daleki od gwiezdnego, ale pojęcie typów nullable jako części języka to kick ass.
P Daddy
193

Oto kilka interesujących ukrytych funkcji C #, w postaci nieudokumentowanych słów kluczowych C #:

__makeref

__reftype

__refvalue

__arglist

Są to nieudokumentowane słowa kluczowe C # (nawet Visual Studio je rozpoznaje!), Które zostały dodane do bardziej efektywnego boksowania / rozpakowywania przed generycznymi. Działają w koordynacji z strukturą System.TypedReference.

Istnieje również __arglist, który jest używany do list parametrów o zmiennej długości.

Jedną z rzeczy, o których ludzie nie wiedzą zbyt wiele, jest System.WeakReference - bardzo przydatna klasa, która śledzi obiekt, ale nadal pozwala zbieraczowi śmieci go gromadzić.

Najbardziej użyteczną funkcją „ukrytą” byłoby słowo kluczowe zwrotu z zysku. Nie jest tak naprawdę ukryty, ale wielu ludzi nie wie o tym. LINQ jest zbudowany na szczycie; pozwala na zapytania wykonywane z opóźnieniem, generując maszynę stanu pod maską. Raymond Chen niedawno napisał o wewnętrznych, szorstkich szczegółach .

Judah Himango
źródło
2
Więcej szczegółów na temat nieudokumentowanych słów kluczowych Petera Bromberga . Nadal nie rozumiem, jeśli istnieją powody, aby kiedykolwiek z nich korzystać.
HuBeZa
@HuBeZa wraz z pojawieniem się generyków, nie ma wielu (żadnych?) Dobrych powodów, aby używać __refType, __makeref i __refvalue. Były one używane przede wszystkim w celu uniknięcia boksu przed generics w .NET 2.
Judah Gabriel Himango
Matt, wraz z wprowadzeniem generycznych w .NET 2, nie ma powodu, aby używać tych słów kluczowych, ponieważ ich celem było unikanie boksu w kontaktach z typami wartości. Zobacz link autorstwa HuBeZa, a także codeproject.com/Articles/38695/UnCommon-C-ke words
Judah Gabriel Himango
185

Związki (rodzaj pamięci współużytkowanej w C ++) w czystym, bezpiecznym języku C #

Bez uciekania się do niebezpiecznego trybu i wskaźników, członkowie klasy mogą dzielić przestrzeń pamięci w klasie / strukturze. Biorąc pod uwagę następującą klasę:

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

Możesz modyfikować wartości pól bajtów, manipulując polem Int32 i odwrotnie. Na przykład ten program:

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

Wyprowadza to:

2147483647
FF FF FF 7F
65535

wystarczy dodać za pomocą System.Runtime.InteropServices;

ZeroBugBounce
źródło
7
@George, działa cuda, gdy komunikujesz się ze starszymi aplikacjami za pośrednictwem gniazd przy użyciu deklaracji Unii Europejskiej.
scottm
2
Warto również powiedzieć int i liczba zmiennoprzecinkowa z przesunięciem 0. To jest potrzebne, jeśli chcesz manipulować liczbami zmiennoprzecinkowymi jako maskami bitowymi, co czasem chcesz. Zwłaszcza jeśli chcesz nauczyć się nowych rzeczy na temat liczb zmiennoprzecinkowych.
John Leidegren,
2
Irytujące jest to, że jeśli zamierzasz użyć tej struktury, kompilator zmusi cię do ustawienia WSZYSTKICH zmiennych w funkcji init. Więc jeśli masz: public A (int int32) {Int32 = int32; } wyrzuci „Pole„ Jeden ”musi być w pełni przypisane, zanim kontrola zostanie zwrócona do dzwoniącego”, więc musisz ustawić Jeden = Dwa = Trzy = Cztery = 0; także.
manixrock
2
Ma to swoje zastosowanie, głównie w przypadku danych binarnych. Używam struktury „Pixel” z int32 @ 0 i czterema bajtami dla czterech komponentów @ 0, 1, 2 i 3. Idealne do szybkiego i łatwego manipulowania danymi obrazu.
snarf
57
Ostrzeżenie: to podejście nie uwzględnia endianizmu. Oznacza to, że Twój kod C # nie będzie działał w ten sam sposób na wszystkich komputerach. W procesorach little-endian (które najpierw przechowują najmniej znaczący bajt) zastosowane zostanie pokazane zachowanie. Ale w procesorach big-endian bajty wyjdą odwrócone od oczekiwanych. Uważaj, jak używasz tego w kodzie produkcyjnym - twój kod może nie być przenośny na niektóre urządzenia mobilne i inny sprzęt i może ulec uszkodzeniu w nieoczywisty sposób (np. Dwa pliki rzekomo w tym samym formacie, ale w rzeczywistości z odwróconą kolejnością bajtów).
Ray Burns,
176

Użycie @ dla nazw zmiennych, które są słowami kluczowymi.

var @object = new object();
var @string = "";
var @if = IpsoFacto(); 
Mark Cidade
źródło
38
Dlaczego chcesz używać słowa kluczowego jako nazwy zmiennej? Wydaje mi się, że dzięki temu kod byłby mniej czytelny i zaciemniony.
Jon
41
Powodem tego jest fakt, że interfejs CLI wymaga go do współdziałania z innymi językami, które mogą używać słów kluczowych C # jako nazw członków
Mark Cidade
69
Jeśli kiedykolwiek chciałeś użyć pomocników HTML HTML MVC asp.net i zdefiniować klasę HTML, z przyjemnością dowiesz się, że możesz użyć @class, aby nie została rozpoznana jako słowo kluczowe klasy
Boris Callens
31
Idealne do metod rozszerzenia. public static void DoSomething (ten Bar @this, string foo) {...}
Jonathan C Dickinson
45
@zihotki: Wrong. var a = 5; Console.WriteLine (@a); Drukuje 5
SLaks
168

Jeśli chcesz wyjść z programu bez wywoływania bloków lub finalizatorów, użyj funkcji FailFast :

Environment.FailFast()
Jan Bannister
źródło
12
Zauważ, że ta metoda tworzy również zrzut pamięci i zapisuje komunikat w dzienniku błędów systemu Windows.
RickNZ
1
Należy zauważyć, że gdy zostanie użyta ta metoda, nie zostaną wywołane żadne finalizatory ani zdarzenia
zwalniające
1
+1 za podpowiedź, ale to nie jest C #, to BCL .NET.
Abel
System.Diagnostics.Process.GetCurrentProcess (). Kill () jest szybszy
Tolgahan Albayrak
153

Zwracanie anonimowych typów z metody i uzyskiwanie dostępu do członków bez refleksji.

// 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
źródło
42
To naprawdę nic ci nie daje. To jest naprawdę niebezpieczne. Co się stanie, jeśli GetUserTuple zostanie zmodyfikowany, aby zwracał wiele typów? Przesyłanie zakończy się niepowodzeniem w czasie wykonywania. Jedną z wielkich zalet C # /. Net jest sprawdzanie czasu kompilacji. O wiele lepiej byłoby po prostu utworzyć nowy typ.
Jason Jackson
9
@Jason Powiedziałem, że to prawdopodobnie nie jest przydatne, ale jest zaskakujące (i myślałem, że jest ukryty).
denis phillips
31
Choć fajnie, wydaje się to raczej kiepskim wyborem projektu. Zasadniczo zdefiniowałeś typ anonimowy w dwóch miejscach. W tym momencie po prostu zadeklaruj prawdziwą strukturę i użyj jej bezpośrednio.
Paul Alexander,
6
@George: taka konwencja nazywa się ... strukturą?
R. Martinho Fernandes
2
ta sztuczka nosi nazwę „rzutuj według próbki” i nie zadziała, jeśli metoda zwracająca typ anonimowy znajduje się w innym zestawie.
desco
146

Oto przydatne dla wyrażeń regularnych i ścieżek plików:

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

@ Mówi kompilatorowi, aby zignorował wszelkie znaki specjalne w ciągu.

Patrick
źródło
27
Również stała @ akceptuje nowe linie wewnątrz. Idealny, gdy przypisujesz skrypt wielowierszowy do łańcucha.
Tor Haugen,
11
Nie zapomnij również o uniknięciu cudzysłowu, po prostu podwoj je, innymi słowy. [code] var candy = @ „Lubię” „czerwone” „laski z cukierkami.”; [/ code]
Dave
5
Zwykle buduję ścieżki za pomocą Path.Combine. Zdecydowanie używam @ do wyrażenia regularnego!
Dan McClain
6
@new jest również zmienną zamiast słowa kluczowego: @this, @int, @return, @interface ... itd. :)
IAbstract
Ten nie zbliża się wcale: ale jakie są najbardziej ukryte funkcje lub sztuczki C #, o których nawet fani C #, uzależnieni, eksperci ledwo wiedzą?
publicgk
141

Mixiny Zasadniczo, jeśli chcesz dodać funkcję do kilku klas, ale nie możesz użyć jednej klasy bazowej dla wszystkich z nich, poproś każdą klasę o wdrożenie interfejsu (bez członków). Następnie napisz metodę rozszerzenia interfejsu , tj

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

Oczywiście poświęcono trochę jasności. Ale to działa!

Dmitri Nesteruk
źródło
4
Tak, myślę, że to jest prawdziwa siła metod rozszerzenia. Zasadniczo pozwalają na implementację metod interfejsu.
John Bubriski
Jest to również przydatne, jeśli używasz NHibernate (lub Castle ActiveRecord) i musisz używać interfejsów do swoich kolekcji. W ten sposób możesz nadać zachowanie interfejsom kolekcji.
Ryan Lundy,
Tak w zasadzie wszystkie metody LINQ są zaimplementowane.
WCWedin
Oto link, który mówi o tym, o czym mówiłem powyżej, przy użyciu metod rozszerzenia z interfejsami kolekcji w NHibernate lub Castle ActiveRecord: devlicio.us/blogs/billy_mccafferty/archive/2008/09/03/...
Ryan Lundy
7
Gdyby tylko zezwolili na właściwości rozszerzenia !!!! Nienawidziłem konieczności pisania metody rozszerzenia, która błagała o właściwość tylko do odczytu ....
John Gibb
130

Nie jestem jednak pewien, dlaczego ktokolwiek miałby chcieć używać Nullable <bool>. :-)

Prawda, fałsz, FileNotFound ?

Michael Stum
źródło
87
jeśli oczekujesz, że użytkownik odpowie na tak, nie, pytanie, to null byłoby właściwe, jeśli pytanie nie zostało udzielone
Omar Kooheji
22
Typy zerowalne są przydatne do interakcji z bazą danych, w której kolumny tabeli są często zerowalne.
tuinstoel
11
Tak, nie, Maybee?
Dan Blair
21
Przechowuj wartości
ThreeState
8
Jak w SQL: Tak / nie / nieznany.
erikkallen
116

Ten nie jest „ukryty”, ale jest źle nazwany.

Wiele uwagi poświęca się algorytmom „mapuj”, „zmniejszaj” i „filtruj”. Większość ludzi nie zdaje sobie sprawy, że .NET 3.5 dodał wszystkie trzy z tych algorytmów, ale nadał im bardzo SQL-owe nazwy, w oparciu o fakt, że są częścią LINQ.

„map” => Wybierz
Przekształca dane z jednej formy w drugą

"zmniejsz" => Agreguj
Agreguje wartości w jeden wynik

„filter” => Where
Filtruje dane na podstawie kryteriów

Możliwość korzystania z LINQ do wykonywania prac inline na kolekcjach, które były używane do iteracji i operacji warunkowych, może być niezwykle cenna. Warto dowiedzieć się, w jaki sposób wszystkie metody rozszerzenia LINQ mogą pomóc w zwiększeniu kompaktowości i łatwości obsługi kodu.

Brad Wilson
źródło
1
Select działa również jak funkcja „return” w monadach. Zobacz stackoverflow.com/questions/9033/hidden-features-of-c#405088
Mark Cidade
1
Użycie „select” jest wymagane tylko w przypadku użycia składni SQLish. Jeśli używasz składni metody rozszerzenia - someCollection.Where (item => item.Price> 5.99M) - użycie instrukcji select nie jest wymagane.
Brad Wilson,
7
@Brad, to (gdzie) jest operacją filtrowania. Spróbuj wykonać operację na mapie bez zaznaczenia ...
Eloff
2
LINQ jest wielką rzeczą, która przydarzyła się C # moim zdaniem: stackoverflow.com/questions/2398818/...
Leniel Maccaferri
1
Najmniejszą sygnaturą agregatu jest funkcja „zmniejszania”, środkowa sygnatura agregatu jest znacznie potężniejszą funkcją „fold”!
Brett Widmeier
115
Environment.NewLine

dla niezależnych od systemu nowych linii.

nt.
źródło
10
Irytujące jest to, że nie jest on zawarty w kompaktowej ramie.
Stormenet,
14
Warto zauważyć, że jest to specyficzne dla platformy hosta aplikacji - więc jeśli tworzysz dane przeznaczone dla innego systemu, powinieneś użyć odpowiednio \ n lub \ r \ n.
Mesh
Jest to część BCL .NET, a nie funkcja C # per se.
Abel,
111

Jeśli próbujesz użyć nawiasów klamrowych w wyrażeniu String.Format ...

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
źródło
19
@ Kyralessa: Właściwie tak, to są nawiasy klamrowe, ale „nawiasy klamrowe” to dla nich nazwa alternatywna. [i ]są nawiasami kwadratowymi, <i >są nawiasami kątowymi. Zobacz en.wikipedia.org/wiki/Bracket .
icktoofay
4
Masz rację. Ale kiedy skomentowałem, nie zawierało nawiasów klamrowych.
Ryan Lundy,
2
Bardzo miło wskazać. Zapamiętaj mój pierwszy String.Format wyglądał następująco: String.Format („{0} Jestem w nawiasach klamrowych {1} {2} {3}", "{", "}", 3, "ślepe myszy"); Kiedy dowiedziałem się, że ucieczka przed nimi odbywa się za pomocą {{i}} byłem bardzo szczęśliwy :)
Gertjan
Muahahahaa ... Programowanie już 3 lata w .Net i tego nie znałem. : D
Arnis Lapsa
„nawiasy klamrowe” działające podobnie jak sekwencje specjalne?
Pratik
104
  1. ?? - operator koalescencyjny
  2. using ( instrukcja / dyrektywa ) - świetne słowo kluczowe, które może być użyte nie tylko do wywołania Dispose
  3. tylko do odczytu - powinien być używany częściej
  4. netmodules - szkoda, że ​​nie ma wsparcia w Visual Studio
Doktor Jones
źródło
6
Można również użyć do aliasu długiej przestrzeni nazw do wygodniejszego ciągu, tj .: używając ZipEncode = MyCompany.UtilityCode.Compression.Zip.Encoding; Tutaj jest więcej: msdn.microsoft.com/en-us/library/sf0df423.aspx
Dave R.
2
To naprawdę nie jest to samo. Podczas wywoływania Dispose możesz użyć instrukcji using, podczas aliasingu typów używasz dyrektywy using.
Øyvind Skaar
2
Na wypadek, gdybyś chciał podać nazwę nr 1 (tak jak w przypadku operatora trójskładnikowego)? nazywa się zerowym operatorem koalescencji.
J. Steen,
4
@LucasAardvark: Jak wspomniał J Steen, nazywa się to zerowym operatorem koalescencji. Szukaj tego!
kokos
1
Szukać ?? operator w google spróbuj: google.com/search?q=c%23+%3F%3F+operator
backslash17
103

@Ed, jestem trochę niechętny publikowaniu tego, ponieważ jest to coś więcej niż nitpicking. Chciałbym jednak zauważyć, że w twoim kodzie przykładowym:

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

Jeśli masz zamiar użyć „jest”, po co po bezpiecznej obsadzie, używając „jako”? Jeśli upewniłeś się, że obj to rzeczywiście MyClass, obsada standardowa:

c = (MyClass)obj

... nigdy nie zawiedzie.

Podobnie możesz po prostu powiedzieć:

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

Nie mam wystarczającej wiedzy o wewnętrznych dodatkach .NET, aby się upewnić, ale instynkt podpowiada mi, że ograniczyłoby to maksymalnie dwie operacje rzutowania typu do maksymalnie jednej. Jest mało prawdopodobne, że tak czy inaczej złamie bank przetwarzania; osobiście uważam, że ta druga forma też wygląda na czystszą.

Dogmang
źródło
16
Jeśli rzutowanie jest dokładnie tego samego typu (rzutowanie na „A”, gdy obiekt to „A”, nie pochodzi od niego), rzutowanie proste jest ~ 3 razy szybsze niż „as”. Podczas rzutowania typu pochodnego (rzut na „A”, gdy obiektem jest „B”, który pochodzi od „A”), rzut prosty jest około 0,1x wolniejszy niż „as”. „jest”, a następnie „jak” jest po prostu głupie.
P Daddy,
2
Nie, ale możesz napisać „if ((c = obj jako MyClass)! = Null)”.
Dave Van den Eynde
10
isi asnie będzie wykonywać rzutowań użytkowników. Tak więc powyższy kod pyta isoperatora, czy obiekt obcy pochodzi z MyClass (czy ma niejawną obsadę zdefiniowaną przez system). Ponadto iszawiesza się null. Oba te przypadki krawędzi mogą być ważne dla twojego kodu. Na przykład możesz chcieć napisać: if( obj == null || obj is MyClass ) c = (MyClass)obj; Ale jest to zupełnie inne niż: try { c = (MyClass)obj; } catch { }ponieważ ten pierwszy nie wykona żadnych konwersji zdefiniowanych przez użytkownika, ale drugi zrobi to. Bez tej nullopcji pierwsza nie ustawi się również, ckiedy objjest null.
Adam Luter
2
W IL rzutowanie odbywa się do CASTCLASS, ale as / do instrukcji ISINST.
John Gibb,
5
Uruchomiłem test, casting IEnumerable<int>do List<int>i casting object( new object()) do IEnumerable<int>, aby upewnić się, że nie ma błędów: rzutowanie bezpośrednie: 5,43ns, is-> as cast: 6,75ns, as cast: 5,69ns. Następnie testowanie nieprawidłowych rzutowań: rzutowanie bezpośrednie: 3125000ns, jako rzutowanie: 5.41ns. Wniosek: przestań się martwić współczynnikiem 1% i po prostu upewnij się, że używasz / jest, gdy rzutowanie może być nieprawidłowe, ponieważ wyjątki (także jeśli są obsługiwane) są BARDZO powolne w porównaniu do przesyłania, mówimy o współczynniku 578000 wolniejszym. Pamiętaj, że ostatnia część, reszta nie ma znaczenia (.Net FW 4.0, wydanie kompilacji)
Aidiakapi
98

Może nie jest to zaawansowana technika, ale taka, którą cały czas widzę, doprowadza mnie do szału:

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

może być skondensowany do:

x = (x==1) ? 2 : 3;
JasonS
źródło
10
Operator trójskładnikowy został zablokowany w mojej grupie. Niektórym po prostu się to nie podoba, chociaż nigdy nie rozumiem dlaczego.
Justin R.
13
Chyba dlatego, że to nie to samo, bycie operatorem, a nie stwierdzeniem. Sam wolę pierwsze podejście. Jest bardziej spójny i łatwy w rozbudowie. Operatory nie mogą zawierać instrukcji, więc w momencie, w którym musisz rozwinąć ciało, będziesz musiał przekonwertować tego trójskładnikowego operatora na instrukcję if
kervin 18.04.2009
16
może być skondensowany do x ++; Ok, to bez sensu ^^
François
5
@Guillaume: Aby uwzględnić wszystkie wartości x: x = 2 + System.Math.Min (1, System.Math.Abs ​​(x-1));
mbeckish
38
To rzeczywiście nazywa się „operator warunkowy” - to operator trójargumentowy ponieważ trwa trzy argumenty. en.wikipedia.org/wiki/Conditional_operator
Blorgbeard jest niedostępny