Enum tanımındaki tilde (~) nedir?


150

Bunca zamandır C # kullandıktan sonra bile bilmediğim şeyleri bulmayı başardım ...

Bunun için internette arama yapmayı denedim, ancak aramada "~" kullanmak benim için pek işe yaramıyor ve MSDN'de de hiçbir şey bulamadım (orada olmadığını söylememe gerek)

Bu kod parçacığını yakın zamanda gördüm, tilde (~) ne anlama geliyor?

/// <summary>
/// Enumerates the ways a customer may purchase goods.
/// </summary>
[Flags]
public enum PurchaseMethod
{   
    All = ~0,
    None =  0,
    Cash =  1,
    Check =  2,
    CreditCard =  4
}

Gördüğüme biraz şaşırdım, bu yüzden derlemeye çalıştım ve işe yaradı ... ama hala ne anlama geldiğini / ne anlama geldiğini bilmiyorum. Herhangi bir yardım??


4
Bu, numaralandırmanın zaman içinde sorunsuz bir şekilde yükseltilmesini sağlayan harika ve zarif bir çözümdür. Maalesef CA-2217 ile çakışır ve kod analizini kullanırsanız bir hata verir :( msdn.microsoft.com/en-us/library/ms182335.aspx
Jason Coyne

Şimdi 2020 ve kendimi gönderinize başladığınız kelimelerle aynı kelimeleri düşünürken buluyorum. Yalnız olmadığımı duyduğuma sevindim.
funkymushroom

Yanıtlar:


136

~ tek terimli birinin tamamlayıcı operatörüdür - işlenenin bitlerini çevirir.

~0 = 0xFFFFFFFF = -1

ikinin tümleyen aritmetiğinde, ~x == -x-1

~ işleci, Objective-C / C ++ / C # / Java / Javascript dahil olmak üzere C'den sözdizimi ödünç almış hemen hemen her dilde bulunabilir.


Çok havalı. Bunu bir enumda yapabileceğinin farkında değildim. Kesinlikle gelecekte kullanacak
Orion Edwards

Yani Tümü = Int32.MaxValue'ye eşdeğer mi? Veya UInt32.MaxValue?
Joel Mueller

2
Tümü = (işaretsiz int) -1 == UInt32.MaxValue. Int32.MaxValue alaka düzeyine sahip değil.
Jimmy

4
@ Stevo3000: Int32.MinValue, 0xF0000000, bu da ~ 0 değil (aslında ~ Int32.MaxValue)
Jimmy

2
@Jimmy: Int32.MinValue 0x80000000. Yalnızca tek bir bit kümesi vardır (F'nin size vereceği dört tanesi değil).
ILMTitan

59

Şöyle düşünürdüm:

[Flags]
public enum PurchaseMethod
{
    None = 0,
    Cash = 1,
    Check = 2,
    CreditCard = 4,
    All = Cash | Check | CreditCard
 }

Biraz daha net olurdu.


10
Bu kesinlikle. Tekli ifadenin tek iyi etkisi, birisi numaralandırmaya eklerse, Tümünün otomatik olarak onu içermesidir. Yine de fayda, netlik eksikliğinden daha ağır basmaz.
ctacke

12
Bunun nasıl daha açık olduğunu anlamıyorum. Fazlalık veya belirsizlik ekler: "Tümü", "tam olarak bu 3'ün kümesi" mi yoksa "bu enum'daki her şey" mi? Yeni bir değer eklersem, bunu Tümü'ne de eklemeli miyim? Başkasının görürseniz henüz All yeni katma değer, yani kasıtlı mı? ~ 0 açıktır.
Ken

16
Kişisel tercih bir yana, eğer ~ 0'ın anlamı OP için sizin ve benim için olduğu kadar açık olsaydı, ilk başta bu soruyu asla sormazdı. Bir yaklaşımın diğerine karşı netliği hakkında ne söylediğinden emin değilim.
Sean Bright

9
C # kullanan bazı programcılar henüz << 'in ne anlama geldiğini bilmediğine göre, daha fazla netlik için bunun etrafında da kod yazmalı mıyız? Bence değil.
Kurt Koller

4
@Paul, bu biraz çılgınca. Kullanmayı bırakalım; İngilizcedir çünkü birçok insan kullanımını anlamaz. Veya bilmedikleri tüm bu kelimeler. Vay canına, bu kadar küçük bir operatör grubu ve kodumuzu bitlerin ne olduğunu ve onları nasıl işleyeceğimizi bilmeyen insanlar için basitleştirmeli miyiz?
Kurt Koller

23
public enum PurchaseMethod
{   
    All = ~0, // all bits of All are 1. the ~ operator just inverts bits
    None =  0,
    Cash =  1,
    Check =  2,
    CreditCard =  4
}

C # 'daki iki tamamlayıcı nedeniyle ~0 == -1, ikili gösterimde tüm bitlerin 1 olduğu sayı.


Ödeme yöntemi için bir bit bayrağı oluşturdukları görülüyor: 000 = Yok; 001 = Nakit; 010 = Kontrol edin; 100 = Kredi Kartı; 111 = Hepsi
Dillie-O

Bu ikinin tümleyeni değildir, bu tüm bitleri ters çevirip bir ekler, böylece ikinin tümleyicisi 0 hala 0 olur. ~ 0 basitçe tüm bitleri veya birinin tümleyicisini ters çevirir.
Beardo

Hayır, ikinin tümleyicisi basitçe tüm bitleri ters çeviriyor.
konfigüratör

2
@configurator - bu doğru değil. Tamamlayanlar, bitlerin basit bir ters çevrilmesidir.
Dave Markle

c #, negatif değerleri temsil etmek için iki tamamlayıcı kullanır. elbette ~ iki tamamlayıcı değildir, sadece tüm bitleri tersine çevirir. Bunu nereden aldığından emin değilim, Beardo
Johannes Schaub - litb

17

Daha iyi

All = Cash | Check | CreditCard

çözüm, çünkü daha sonra başka bir yöntem eklerseniz şunu söyleyin:

PayPal = 8 ,

tilde-All ile işiniz zaten bitmiş olacak, ancak all-line'ı diğeriyle değiştirmeniz gerekiyor. Bu yüzden daha sonra hataya daha az meyillidir.

Saygılarımızla


Ayrıca neden daha az hataya meyilli olduğunu söylerseniz daha iyi olur. Örneğin: Eğer değeri bir veritabanı / ikili dosyada depolar ve ardından numaralandırmaya başka bir bayrak eklerseniz, bu 'Tümü'ne dahil edilecektir, bu da' Tümü'nün her zaman tümü anlamına geleceği anlamına gelir ve yalnızca bayraklar aynı :).
Aidiakapi

11

Sadece bir yan not, kullandığınızda

All = Cash | Check | CreditCard

tüm değerleri içerirken herkese eşit olmayan başka bir değere (-1) değil (-1) olarak Cash | Check | CreditCarddeğerlendirilecek ek avantajınız var All. Örneğin, kullanıcı arayüzünde üç onay kutusu kullanırsanız

[] Cash
[] Check
[] CreditCard

ve değerlerini toplar ve kullanıcı hepsini seçer All, sonuçta ortaya çıkan numaralandırmada görürsünüz .


Bu yüzden myEnum.HasFlag()onun yerine şunu kullanıyorsunuz: D
Pyritie

@ Pyritie: Yorumunuzun söylediklerimle ne ilgisi var?
konfigüratör

olduğu gibi ... "Tümü" için ~ 0 kullanırsanız, buna benzer bir şey yapabilirsiniz All.HasFlag(Cash | Check | CreditCard)ve bu doğru olarak değerlendirilir. Her ==zaman ~ 0 ile çalışmadığından geçici bir çözüm olabilir .
Pyritie

Oh, ben hata ayıklayıcıda gördükleriniz hakkında konuşuyordum ToString- kullanmaktan değil == All.
konfigüratör

9

Bu soruyu aydınlatıcı bulan diğerleri ~için paylaşacağım hızlı bir örnek var . Bu Mono belgelerinde ayrıntılı olarak anlatıldığı gibi, bir boyama yönteminin uygulanmasından elde edilen aşağıdaki pasaj, ~harika bir etki için kullanır :

PaintCells (clipBounds, 
    DataGridViewPaintParts.All & ~DataGridViewPaintParts.SelectionBackground);

~Operatör olmadan, kod muhtemelen şuna benzer:

PaintCells (clipBounds, DataGridViewPaintParts.Background 
    | DataGridViewPaintParts.Border
    | DataGridViewPaintParts.ContentBackground
    | DataGridViewPaintParts.ContentForeground
    | DataGridViewPaintParts.ErrorIcon
    | DataGridViewPaintParts.Focus);

... çünkü numaralandırma şöyle görünür:

public enum DataGridViewPaintParts
{
    None = 0,
    Background = 1,
    Border = 2,
    ContentBackground = 4,
    ContentForeground = 8,
    ErrorIcon = 16,
    Focus = 32,
    SelectionBackground = 64,
    All = 127 // which is equal to Background | Border | ... | Focus
}

Bu sayımın Sean Bright'ın cevabına benzerliğine dikkat edin?

Sanırım benim için en önemli ~çıkarım, bir numaralandırmada normal bir kod satırında olduğu gibi aynı operatördür.


İkinci kod bloğunuzda &s olması gerekmiyor |mu?
ClickRick

@ClickRick, yakaladığınız için teşekkürler. İkinci kod bloğu artık mantıklı geliyor.
Mike


1

@ Sean Bright'ın cevabıyla aynı şeyi yapan ancak bana daha iyi görünen kişisel olarak kullandığım alternatif şudur:

[Flags]
public enum PurchaseMethod
{
    None = 0,
    Cash = 1,
    Check = 2,
    CreditCard = 4,
    PayPal = 8,
    BitCoin = 16,
    All = Cash + Check + CreditCard + PayPal + BitCoin
}

Uyarı ikisinin tüm güçler bu sayılar, ikili doğası, şu iddia doğru yapar nasıl: (a + b + c) == (a | b | c). Ve IMHO, +daha iyi görünüyor.


1

~ İle biraz deneyler yaptım ve tuzakları olabileceğini buldum. LINQPad için bu pasajı göz önünde bulundurun, tüm değerler birlikte orantılı olduğunda All enum değerinin beklendiği gibi davranmadığını gösterir.

void Main()
{
    StatusFilterEnum x = StatusFilterEnum.Standard | StatusFilterEnum.Saved;
    bool isAll = (x & StatusFilterEnum.All) == StatusFilterEnum.All;
    //isAll is false but the naive user would expect true
    isAll.Dump();
}
[Flags]
public enum StatusFilterEnum {
      Standard =0,
      Saved =1,   
      All = ~0 
}

Standart 0 değerini almamalı, ancak 0'dan büyük bir değer almalıdır
Adrian Iftode

1

[İşaretler] numaralandırmasındaki her bit, etkinleştirilmiş ( 1) veya devre dışı bırakılmış ( 0) bir şey anlamına gelir .
~operatör, sayının tüm bitlerini ters çevirmek için kullanılır. Örnek: 00001001bdönüşüyor 11110110b.
Böylece ~0, 11111111b8 bitlik numaralandırma gibi, tüm bitlerin etkinleştirildiği değeri yaratmak için kullanılır .

Bu tür numaralandırmalar için bitsel sola kaydırma operatörünün aşağıdaki gibi kullanılmasının daha uygun olabileceğini eklemek isterim:

[Flags]
enum SampleEnum
{
    None   = 0,      // 0000b
    First  = 1 << 0, // 0001b
    Second = 1 << 1, // 0010b
    Third  = 1 << 2, // 0100b
    Fourth = 1 << 3, // 1000b
    All    = ~0      // 1111b
}

Bu soruya hiç cevap vermiyor.
2017'de
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.