2009-06-04 7 views
2

Je viens de trouver un peu de quelqu'un de code ici avait écrit pour accéder à certaines entités DB ...Utiliser `où T: SOMETHING` construire en C#

public static OurCustomObject GetOurCustomObject(int primaryKey) 
{ 
    return GetOurCustomObject<int>(primaryKey, "usp_GetOurCustomObjectByID"); 
} 

public static OurCustomObject GetOurCustomObject(Guid uniqueIdent) 
{ 
    return GetOurCustomObject<Guid>(uniqueIdent, "usp_GetOurCustomObjectByGUID"); 
} 

private static OurCustomObject<T>(T identifier, string sproc) 
{ 

    if((T != typeof(int)) && (T == typeof(Guid))) 
    { 
     throw new ArgumentException("Identifier must be a string or an int"); 
    } 

    //ADO.NET Code to make DB Call with supplied sproc. 
} 

Theres juste quelque chose qui ne semble pas très generic. Le fait que les sprocs sont passés dans la méthode intérieure est moche. mais la seule façon que je peux voir autour de ce qui est d'avoir un if/else dans la méthode privée le long des lignes de

if(type == int) 
    sproc = "GetByID"; 
else if (type == Guid) 
    sproc = "GetByGUID"; 

également le lancement d'exception est moche et ... est là de toute façon d'utiliser un où T : clause

par exemple

private static OurCustomObject<T>(T identifier) where T : int OR Guid 

Toutes les suggestions sur la façon de nettoyer un peu.

+0

Qu'est-ce que vous utilisez « T » dans le corps de la méthode? –

+0

C'est juste que ce n'était pas spécifiquement utilisé. Il y a eu la vérification typeof (T) == typeof (int), puis après cela, ils exécutaient simplement le SPROC en utilisant un appel SQL Helper et en passant simplement l'identifiant en tant que paramètre. C'est pourquoi je pensais juste que l'utilisation se sentait un peu forcé –

+0

Ensuite, je suis d'accord avec vous que cela semble être un mauvais design. Le but des méthodes génériques est d'être, bien, GENERIC. Un "générique" qui ne peut être que deux choses n'est pas très générique. Si vous n'utilisez pas T pour autre chose, alors la seule chose que vous pouvez éventuellement utiliser "identifiant" - un T non contraint dans le corps est un System.Object. Pourquoi ne pas éliminer complètement la généricité et simplement faire de "identifiant" un objet? –

Répondre

8

Vous ne pouvez pas spécifier une contrainte qui dit "c'est l'un de ces deux", non.

Ce que vous pourrait faire est:

Dictionary<Type, string> StoredProcedureByType = new Dictionary<Type, string> 
{ 
    { typeof(Guid), "GetByGUID" }, 
    { typeof(int), "GetByID" } 
}; 

Ensuite, utilisez:

string sproc; 
if (!StoredProcedureByType.TryGetValue(typeof(T), out sproc)) 
{ 
    throw new ArgumentException("Invalid type: " + typeof(T).Name); 
} 

C'est probablement excessif pour seulement deux types, mais il échelles bien si vous avez beaucoup de types impliqués.

Étant donné que les deux types sont des types de valeur, vous pouvez faire un peu plus robuste avec une contrainte de:

where T : struct 

mais qui va tout de même byte etc.

+0

À la votre, Jon. –

0

Il n'y a pas de support pour une telle clause où limiter le paramètre de type de cette façon (malheureusement).

Dans ce cas, vous pourriez constater que le passage identifier comme un objet, et pas en utilisant des médicaments génériques est en fait plus propre (quand je avais besoin quelque chose de semblable, c'est-ce que je fini par faire: semblait être la moins pire approche) .

C'est une zone où C# est faible, n'étant ni un langage dynamique ni capable de spécialiser le template (comme C++, fournissant seulement des implémentations pour T = int et T = Guid). Adenda: Dans ce cas, je serais probablement coller avec la surcharge, mais changer le contrôle de type à un Assert comme c'est une méthode d'aide privée.

+0

Les types génériques ne sont pas des modèles et ne sont pas destinés à l'être. On pourrait faire valoir que les modèles C++ sont "faibles" car, contrairement aux types génériques, ils ne peuvent pas être construits avec un nouvel argument de type après que le modèle a été compilé dans une bibliothèque. Mais ce serait un argument stupide; le fait simple est que les génériques et les modèles, bien que superficiellement similaires textuellement, ont en réalité une sémantique très différente et résolvent différents problèmes. –

+0

@Eric: C'est vrai, mais cela ne s'arrête pas là où une spécialisation serait vraiment utile. – Richard

2

Le code que vous avez fourni me semble relativement correct, car private static OurCustomObject<T>(T identifier, string sproc) est privé. Je supprimerais même la vérification d'exception de cette méthode, car encore une fois - son privé, donc cette classe contrôle ce qui est passé à la méthode. La déclaration if serait plutôt horrible et sur-machinée.

1

La neatest chose à faire est probablement faire quelque chose comme ceci:

public interface IPrimaryKey 
{ 
} 

public class PrimaryGuidKey(Guid key) : IPrimaryKey 

public class PrimaryIntegerKey(int key) : IPrimaryKey 

private static OurCustomObject<T>(T identifier) where T : IPrimaryKey 
Questions connexes