2010-07-20 69 views
9

J'essaye de créer une pince simple (de sorte que je puisse lier les valeurs de n'importe quoi comparable ... la plupart du temps pour les types de nombres tels qu'int, double, etc.)Type générique nullable utilisé avec IComparable. C'est possible?

Le problème est si je fais ceci Je reçois une erreur, mais according to MSDN CompareTo IComparable est censé être capable de gérer les valeurs nulles.
Citation: "Par définition, tout objet compare plus de null, et deux références nulles se comparent les unes aux autres."

public static T Clamp<T>(this T value, T min, T max) 
    where T : IComparable<T> 
{ 
    if (value.CompareTo(max) > 0) 
     return max; 

    if (value.CompareTo(min) < 0) 
     return min; 

    return value; 
} 



private Int32? _zip; 
public Int32? Zip 
{ 
    get 
    { 
     return _zip; 
    } 
    set 
    { 
     _zip = value.Clamp<Int32?>(0, 99999); 
    } 
} 

Répondre

7

Rappelez-vous, Int32? est un raccourci pour Nullable<Int32>. Puisque Nullable<T> n'implémente pas IComparable<T>, votre code, tel que structuré, ne sera pas compilé.

Vous pouvez toutefois surcharger la méthode:

public static T? Clamp<T>(this T? value, T? min, T? max) 
    where T : struct, IComparable<T> 
{ 
    // your logic... 
} 

Bien sûr, si vous avez l'intention de travailler avec les types nullable, vous devez définir comment vous serrer null valeurs ...

Si vous n'avez pas réellement besoin de serrer null les valeurs, il peut être plus simple de vérifier d'abord null dans votre getter de la propriété:

public Int32? Zip 
{ 
    ... 
    set 
    { 
     _zip = value == null ? value : value.Value.Clamp<Int32>(0,99999); 
    } 

Ou mieux encore, faire partie de la mise en œuvre de la surcharge supplémentaire pour Clamp ...

+0

Je ne sais pas pourquoi je ne l'ai pas fait (value == null)? valeur: valeur.Clamp (0, 99999); pour commencer. Je suppose que j'essayais juste de forcer la pince à le faire automatiquement. Mais oui, il est effectivement plus logique de ne pas l'annuler, car c'est le serrage. –

+0

Et maintenant ça ne marchera pas pour la chaîne))) –

12

Comme l'a dit @LBushkin Nullable < T> ou T? n'implémente pas l'interface IComparable. La solution donnée est correcte, mais je préfère avoir la logique de comparaison Nullable dans une classe spécialisée en la matière, après le Single Responsibility Principle, et aussi que peut être utilisé pour comparer n'importe quel type Nullable.

Par exemple, vous pouvez créer une classe comparateur de type Nullable générique comme ceci:

public class NullableComparer<T> : IComparer<Nullable<T>> 
     where T : struct, IComparable<T> 
{ 

    public int Compare(Nullable<T> x, Nullable<T> y) 
    { 
     //Compare nulls acording MSDN specification 

     //Two nulls are equal 
     if (!x.HasValue && !y.HasValue) 
      return 0; 

     //Any object is greater than null 
     if (x.HasValue && !y.HasValue) 
      return 1; 

     if (y.HasValue && !x.HasValue) 
      return -1; 

     //Otherwise compare the two values 
     return x.Value.CompareTo(y.Value); 
    } 

} 

Dans ce cas, vous devez utiliser cette classe comme ceci:

public static T? Clamp<T>(this T? value, T? min, T? max) 
    where T : struct 
{ 
    var comparer = new NullableComparer<T>(); 

    if (comparer.Compare(value, max) > 0) 
     return max; 

    if (comparer.Compare(value, min) < 0) 
     return min; 

    return value; 
} 

Handy pour sauver vos aides bibliothèque.

J'espère que ça aide!