8
votes

"Promouvoir" type générique à Nullable en C #?

donné un type générique t in c #, je me demande comment acquérir le type q , qui est égal à t? pour non-nullable t et t pour déjà nullable t .

La question se pose d'un code réel. Je souhaite unifier l'accès aux paramètres passés via la chaîne de requête dans mon application ASP.NET. Et je souhaite spécifier une valeur par défaut du même type, mais assurez-vous que null peut être transmis comme une valeur par défaut. xxx

actuellement je suis forcé ayant deux surcharges du fetchvalue - une sans valeur par défaut et une avec elle: xxx

Ça fonctionne bien, mais je me demande s'il est possible de fusionner les deux fonctions comme celle-ci.

en C ++, j'utiliserais des traits de type, tels que PROMOCULLULLABLABLE :: TYPE avec deux spécialisations de PROMOTELLULLABLE Pour des types nullables et non nullables. Mais qu'en est-il de C #?


7 commentaires

Personnellement, je pense que le code est plus clair du site d'appel si vous avez simplement deux surcharges.


@Matthewwatson Le code du site d'appel est exactement le même pour les deux implémentations.


Le compilateur lutterait pour générer un code approprié pour retourfild_value; si par défaut_value était-ce "peut-être un t , peut-être un t? < / code> "type. Même retour (t) par défaut_value; nécessite un code différent s'il s'agit d'un t? . N'oubliez pas que le compilateur doit générer un seul code qui convient à toutes les instanciations.


@Damien_the_unbeliever Que voulez-vous dire que le code généré serait le même pour différentes instanciations? .. ce n'est pas vrai pour les modèles C ++. Est-ce vrai pour C # Generics?


Les génériques C # sont une fonction d'exécution, et non une fonction de compilation de la compilation que dans les modèles de C ++. En tant que tel, le compilateur doit générer une séquence une d'IL qui sera valide pour tout paramètre de type fourni. (Il existe une certaine variation dans la manière dont l'IL est traduite en code de machine à JIT Time, en fonction des types de valeur / de référence, mais l'IL est toujours identique)


À titre de note de côté - j'irais probablement avec les deux surcharges, mais j'ai probablement une méthode interne privée Bool TryFetStetvalue (nom de chaîne, valeur REF T) que les deux appellent. Il fait le travail sale, puis tous les méthodes fetchvalue sont soit renvoyer la valeur par défaut, soit lancer une exception (selon la surcharge qu'ils sont).


@Damien_the_unbeliever Je ne le savais pas, merci. Concernant tryfetchvalue - Voici comment je l'ai réellement mis en œuvre :)


6 Réponses :


0
votes

Essayez ce qui suit:

var val = FetchValue<NullableString>("blah", new NullableString() { Value = "default" });


3 commentaires

Je veux que cette fonction fonctionne avec des types nullables, du moins avec des chaînes.


Cela obtient un code sur le site de l'appelant, donc je ne pense pas que ce soit pratique. Belle pensée à envelopper string dans struct cependant.


@Mikhail Je suppose que vous pourriez avoir les appels à cette fonction proxé, donc en cas de transmission d'un type de chaîne, cela concernera dans cette structure. Mais je suis d'accord, ça va commencer à devenir très désordonné à partir de là.



3
votes

ne répond pas directement à la question posée, mais j'écrirais ceci: xxx pré>

de sorte que la majeure partie du code n'existe qu'une seule fois - et vous pouvez même en réalité avoir l'appel code choisissez d'avoir null code> comme une valeur par défaut, s'il le choisit. p>


Même si vous pouviez créer la déclaration de paramètre que vous vouliez, cette ligne serait toujours un problème : p> xxx pré>

s'il s'est avéré que par défaut_value code> était un t? code> plutôt qu'un t code >, alors le code ci-dessus ne fonctionne pas. Même si vous faites un casting: p>

return (T)default_value;


2 commentaires

C'est presque comment il est actuellement mis en œuvre :)


Ok, je n'ai pas reçu de réponse à ma question, mais j'ai une explication pourquoi il n'y a pas de réponse. Merci, j'accepte cela.



0
votes

Vous pouvez spécifier la contrainte Classe pour vous assurer que les appelants passent dans un type de référence (qui est nullable): xxx

Dans ce cas, vous déléguez la responsabilité de casting t à t? Si nécessaire à l'appelant, qui est gentil de toute façon: il peut savoir mieux!


1 commentaires

Identique à ce que @terric - je veux vraiment des types nullables et non nullables (structs de classe AS) autorisés. Au moins int s et chaîne s.



1
votes

Étant donné que votre type de retour est t code> et t code> Beeing a un type de valeur, il ne peut pas être null. Donc, vous devrez toujours passer un type nullable, car vous voulez un retour nul, non?

Essayez celui-ci, il vous permet de passer un type de valeur nullable ( i code>) et une normale Type de référence ( O CODE>): P>

static void Main(string[] args)
{
    int? i = 5;
    object x = new object();

    object o = FetchValue("x", i);
    o = FetchValue("x", x);
}

private static T? FetchValue<T>(string name, T? p) where T : struct
{
    T? result = (T?)FetchValue(name, (object)p);
    return result;
}

private static T FetchValue<T>(string name,
    T default_value = default (T)) // default(T) where T is a reference type will always be null!
    where T : class
{
    // do whatever you want
    var page = HttpContext.Current.Handler as Page;

    string str = page.Request.QueryString[name];

    if (str == null)
    {
        if (default_value == null)
        {
            throw new HttpRequestValidationException("A " + name + " must be specified.");
        }
        else
        {
            return default_value;
        }
    }

    return (T)Convert.ChangeType(str, typeof(T));
}


2 commentaires

Dans ce cas, je ne peux pas utiliser fetchvalue ("x") sans valeur par défaut. Je ne veux pas de null retour, je veux une valeur par défaut ou une exception.


Lorsque vous utilisez fetchvalue ("x") et ayant un type de retour de t Vous ne pouvez pas récupérer NULL en arrière car int n'est pas un type de référence. Lorsque vous parlez de default_value je vous ai appris que vous voulez toujours une référence / nullable type de retour. (Qu'en est-il de bien sûr ne pas très malheur, car vous utiliseriez objet comme type de retour alors)



0
votes

Vous pouvez faire ce que vous voulez en cette manière simple:

public static T FetchValue<T>(string name, T defaultValue = null) where T : class { }
public static T? FetchValue<T>(string name, T? defaultValue = null) where T : struct { }


2 commentaires

Cela nécessite deux surcharges, alors que j'ai déjà une solution avec deux surcharges, ce que je trouve plus facile.


@Mikhail C'est la voie la plus simple et la plus efficace.



1
votes

Il y a mon exemple: xxx


3 commentaires

Belle pensée à faire une fonction d'extension. Mais le fetchvalue n'est pas de type sécurisé, par ex. On peut appeler fetchvalue ("id", "foo") , et cela compilera.


De plus, je ne comprends pas pourquoi fetchvalue et fetchvaluefromCurrentPage n'a pas de prototypes symétriques. Il est impossible d'utiliser des structures sans valeurs par défaut pour ce dernier, comme celui-ci: pageeextensions.fetchvaluefromcurrentPage ("id") .


Je pense que vous avez besoin de quelque chose comme 'renvoyer défaut (t)' dans le cas de T est valuetype. Et dans votre exemple: la valeur null ne peut pas être une valeur par défaut - ce n'est pas une bonne pratique. Je veux renvoyer la valeur nulle comme valeur par défaut par exemple.