2010-02-02 15 views
8

Chcę móc sprawdzić, czy wartość jest domyślna dla jej typu wartości. Idealnie, chciałbym powiedzieć:Domyślna kontrola wartości przy użyciu typów ogólnych

DoSomething<TValue>(TValue value) { 
    if (value == default(TValue)) { 
     ... 
    } 
} 

Jednak kompilator narzeka, że ​​nie może zrobić porównanie == na TValue i TValue. Jest to najlepszy obejście że wpadliśmy do tej pory:

DoSomething<TValue>(TValue value) { 
    if (value == null || value.Equals(default(TValue))) { 
     ... 
    } 
} 

Czy istnieje bardziej elegancki/poprawny sposób, aby przejść na ten temat?

Odpowiedz

37
public bool EqualsDefaultValue<T>(T value) 
{ 
    return EqualityComparer<T>.Default.Equals(value, default(T)); 
} 
4

Wrzuć ograniczenie klasy i powinno działać.

public void Test<T>(T instance) where T : class 
{ 
    if (instance == default(T)) 
    { 

    } 
} 

Lub jeśli chcesz tylko typy wartości, możesz to zrobić.

public void Test<T>(T instance) where T : struct, IEquatable<T> 
{ 
    if (instance.Equals(default(T))) 
    { 

    } 
} 
+4

Jeśli rzucisz o ograniczeniu 'class' możesz równie dobrze sprawdzić' null' ... –

+0

@Marc - Wiem, w tym momencie jest to kwestia preferencji. – ChaosPandion

+0

Mam nadzieję, że uda mi się to zrobić dla każdego typu, w tym wartości i typów klas. – StriplingWarrior

1

Twój problem jest, że typ genric (bez ograniczeń) musi być "compilable" dla dowolnego typu. Ponieważ nie wszystkie typy mają operatora ==, twój kod się nie skompiluje.

Jednym ze sposobów rozwiązania tego problemu jest dodanie ograniczenia klasy, ponieważ domyślne ustawienie (TValue) sugeruje, że kod ma współpracować z innymi typami. (W przeciwnym razie po prostu używać NULL zamiast domyślnej (TValue). Jednym z rozwiązań może być coś podobnego do tego, co Bryan Watts sugeruje

bool DoSomething<TValue>(TValue value) { 
    return EqualityComparer<TValue>.Default.Equals(value, default(TValue)); 
} 

czy można owinąć go w metodę rozszerzenia

bool IsDefault<TValue>(this TValue value) { 
    return EqualityComparer<TValue>.Default.Equals(value, default(TValue)); 
} 
0

dodanie do odpowiedzi wysłane tutaj, myślę, że powinniśmy także być w stanie określić, czy chcemy czy wartość referencyjną równości:

static public class MyGenericHelper 
{ 
    static public bool EqualsByValue<T>(T x, T y) 
    { 
     return EqualityComparer<T>.Default.Equals(x, y); 
    } 

    static public bool EqualsByReference<T>(T x, T y) 
    { 
     if (x is ValueType) return EqualityComparer<T>.Default.Equals(x, y) // avoids boxing 

     return Object.ReferenceEquals(x, y); 
    } 
} 

wszyscy uwielbiają tworzyć i utrzymywać zillion trochę h Takimi metodami nie mamy: ->

+0

Wygląda na to, że 'typeof (T) .IsValueType' byłby lepszy niż' x jest ValueType' zarówno pod względem czytelności, jak i wydajności. Czy się mylę? – StriplingWarrior

+1

Może czytelność jest czasem kwestią gustu. : -> ... 'x jest ValueType' po prostu intuicyjnie" czuje się szybciej "dla mnie, ponieważ' typeof (T) .IsValueType' musi odzyskać dość ciężką instancję typu "Type", a Reflector ujawnia, że ​​'.IsValueType' właściwość wywołuje wirtualną metodę, która zaimplementowana w 'RuntimeType' ostatecznie wywołuje coś zewnętrznego. Ale kim ja jestem, by sądzić, nie jestem osobą wewnętrzną. Mogą nawet skompilować się do tych samych instrukcji. Być może to dobre nowe pytanie. ;-) ... Albo sam mógłbyś wykonać testy wydajności, jeśli jest to kluczowe. – herzmeister

+0

To nie jest najważniejsze, ale byłem ciekawy, więc zrobiłem szybki test i masz rację! 'x to ValueType' zajmuje około trzeciej długości. – StriplingWarrior

Powiązane problemy