Je cherche un moyen d'éviter les surcharges verbeuses dans le cas d'une méthode qui 1) convertira toujours le paramètre en chaîne et 2) devrait être disponible pour transmettre d'autres types comme paramètre.

Comme toujours un extrait image vaut mille mots et a trouvé la solution suivante resp. exemple: (c'est-à-dire en utilisant un paramètre d'objet, qui est converti en IFormattable pour transmettre une culture invariante)

public static string AppendHexSuffix(this object hexNumber)
{
    string hexString = (hexNumber as IFormattable)? // Cast as IFormattable to pass inv cul
        .ToString(null, CultureInfo.InvariantCulture) ?? hexNumber.ToString();
    if (!hexString.StartsWith("0x", true, CultureInfo.InvariantCulture)
        && !hexString.EndsWith("h", true, CultureInfo.InvariantCulture))
    {
        hexString += "h"; // Append hex notation suffix, if missing prefix/suffix
    }
    return hexString;
}

Bien que, d'après ce que j'ai testé, cela semble fonctionner correctement (corrigez-moi si je manque quelque chose), le code supérieur ne me semble pas particulièrement simple et préférerait une solution plus intuitive.

Question finale: existe-t-il une autre manière plus élégante de résoudre ce 1) sans utiliser l'approche du paramètre d'objet supérieur et 2) sans déclarer explicitement une surcharge pour chaque type?

Remarque: l'extrait supérieur doit être pris strictement à titre d'exemple, car la partie de l'instruction if n'a de sens que dans le cas où une "vraie" chaîne est passée en paramètre.


MODIFIER: après avoir pris en compte les réponses et les commentaires que j'ai obtenus, et après quelques essais supplémentaires, la mise en œuvre finale suivante semble être la mieux adaptée à mon cas:

/// <summary>
/// Converts any type to string and if hex notation prefix/suffix is missing
/// yet still a valid hex number, then appends the hex suffix
/// </summary>
/// <param name="hexNumber">Takes a string, decimal and other types as parameter</param>
/// <returns>Returns input object content as string with hex notation</returns>
public static string AppendHexSuffix<T>(this T hexNumber)
{
    // Cast as IFormattable to pass hex format ("X") & invariant culture
    var hexString = (hexNumber as IFormattable)?
        .ToString("X", CultureInfo.InvariantCulture).Trim() ?? hexNumber.ToString().Trim();
    int unused;
    if (!hexString.StartsWith("0x", true, CultureInfo.InvariantCulture)
        && !hexString.EndsWith("h", true, CultureInfo.InvariantCulture)
        && int.TryParse( // No hex prefix/suffix, but still a valid hexadecimal number
            hexString, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out unused))
    {
        hexString += "h";
    }
    return hexString;
}

Par cela, il doit également ignorer les paramètres / objets où le format hexadécimal n'a aucun sens, comme indiqué dans le commentaire de @InBetween. Erreur notable: j'ai oublié le format "X" pour le premier appel .ToString ().

1
Teodor Tite 17 janv. 2017 à 19:33

2 réponses

Meilleure réponse

Vous pouvez utiliser ici des génériques et des paramètres d'interface pour toujours obtenir les meilleures performances disponibles (éviter la boxe) et garder votre code SEC.

Commençons par notre code commun.

private static string appendHexSuffix(this string hexString)
    if (!hexString.StartsWith("0x", true, CultureInfo.InvariantCulture)
        &&
        !hexString.EndsWith("h", true, CultureInfo.InvariantCulture))
    {
        hexString += "h";
    }
    return hexString;
}

Ensuite, fournissons deux surcharges. Tout d'abord, un pour IFormattable.

public static string AppendHexSuffix(this IFormattable hexNumber) =>
    appendHexSuffix(hexNumber.ToString(null, CultureInfo.InvariantCulture));

Maintenant, notre générique pour quand cette signature de méthode ne correspond pas à ce que nous transmettons.

public static string AppendHexSuffix<T>(this T hexNumber) =>
    appendHexSuffix(hexNumber.ToString());

Puisque T peut être implicite, nous n'aurons pas besoin de le spécifier, prétendez simplement qu'il n'y a pas de générique du tout.

Un peu hors sujet mais je me demande si vous voulez vraiment que cette méthode accepte n'importe quel type d'objet. Vous pourriez être mieux avec une contrainte générique qui spécifie chaque type que vous souhaitez éventuellement accepter. Ou peut-être une interface. En savoir plus sur les contraintes génériques ici: https://msdn.microsoft.com/en- us / library / d5x73970.aspx

Maintenant, vous avez demandé comment faire cela sans surcharge, je vais donc l'ajouter également, mais je ne le recommande pas.

public static string AppendHexSuffix<T>(this T hexNumber)
{
    var hexString = (hexNumber as IFormattable)?
        .ToString(null, CultureInfo.InvariantCulture) ?? hexNumber.ToString();
    if (!hexString.StartsWith("0x", true, CultureInfo.InvariantCulture)
        && !hexString.EndsWith("h", true, CultureInfo.InvariantCulture))
    {
        hexString += "h"; // Append hex notation suffix, if missing prefix/suffix
    }
    return hexString;
}
1
Teodor Tite 19 janv. 2017 à 12:53

Je pense que vous feriez mieux d'utiliser des expressions régulières pour quelque chose comme ça.

Premièrement, Convert.ToString sera déjà formatez l'objet comme vous le souhaitez, afin de pouvoir remplacer ce code.

Pour convertir value en sa représentation sous forme de chaîne, la méthode tente d'appeler l'implémentation IConvertible.ToString de value. Si value n'implémente pas l'interface IConvertible, la méthode tente d'appeler l'implémentation IFormattable.ToString de value. Si value n'implémente pas l'interface IFormattable, la méthode appelle la méthode ToString du type de valeur sous-jacent.

Deuxièmement, lorsque vous avez besoin d'une chaîne pour avoir une certaine apparence, les expressions régulières sont presque toujours la solution. Ils sont également très rapides si vous les compilez. 99% du temps, vous souhaitez placer un objet d'expression régulière dans une variable statique pour cette raison. J'utilise Regexr pour tester les expressions régulières. Il a l'avantage supplémentaire d'exclure gracieusement lorsque la chaîne renvoyée n'est certainement pas une chaîne hexadécimale.

private static Regex _parser = new Regex(@"(0x)?((\d{2})+)h?", RegexOptions.Compiled);
public static string AppendHexSuffix(this object hexNumber)
{
  var hexString = Convert.ToString(hexNumber);
  var match = _parser.Match(hexString);
  if (!match.Success)
    throw new FormatException("Object cannot be converted to a hex format");
  return match.Groups[2].Value + "h";
}

#if DEBUG
public static void AppendHexSuffixTest()
{
  AppendHexSuffixTest("0x121212", "121212h");
  AppendHexSuffixTest("0x121212h", "121212h");
  AppendHexSuffixTest("121212h", "121212h");
}

public static void AppendHexSuffixTest(object test, string expected)
{
  if (test.AppendHexSuffix() != expected)
    throw new Exception("Unit test failed");
}
#endif 
1
Robear 19 janv. 2017 à 17:56