【问题标题】:How to avoid method overloads for multiple parameter types (else than by using object parameter)?如何避免多种参数类型的方法重载(除了使用对象参数)?
【发布时间】:2017-01-17 16:33:32
【问题描述】:

我正在寻找一种方法如何避免冗长的重载,以防 1) 将始终将参数转换为字符串并且 2) 应该可用于传递其他类型作为参数。

一如既往一张图片 sn-p值一千字,并找到了以下解决方案。示例:(即通过使用对象参数,其中is cast to IFormattablepassing invariant culture

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;
}

虽然,据我测试,它似乎工作正常(如果我遗漏了什么,请纠正我),上面的代码对我来说并不是特别直接,而是更喜欢更直观的解决方案。

最后一个问题:有没有其他更优雅的方法来解决这个1)而不使用上层对象参数方法和2)没有为每种类型显式声明重载?

注意:上面的 sn-p 应该严格作为示例,因为if-statement 部分只有在“真实”字符串作为参数传递的情况下才有意义。


编辑:在考虑了我得到的答案 + cmets 后,经过更多试验,以下最终实现似乎最适合我的情况:

/// <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;
}

由此,它还应该忽略十六进制格式没有意义的参数/对象,正如@InBetween 的评论中所指出的那样。值得注意的错误:忘记了第一个 .ToString() 调用的“X”格式。

【问题讨论】:

  • AppendHexSuffix&lt;T&gt;(this T hexNumber) where T : IFormattable?
  • 您可能会考虑将其发布到Code Review,因为您本身似乎并不是在寻求帮助来解决问题。
  • 您当前的解决方案将输入框和值类型,这是泛型可以避免的。无论如何,如果你传入一个十六进制格式没有意义的对象会发生什么?仿制药和您当前的解决方案都不会保护您免受这种情况的影响。
  • @devlin carnate:你的意思是也从 SO 中删除它?
  • @proGrammar :我只是说它可能更适合 CR 而不是 SO。

标签: c# casting hex overloading boilerplate


【解决方案1】:

您可以在此处使用泛型和接口参数,以始终获得最佳性能(避免装箱)并保持代码 DRY。

让我们从我们的通用代码开始。

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

接下来让我们提供两个重载。首先,一个用于 IFormattable。

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

现在我们的通用方法签名与我们传入的不匹配。

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

由于 T 可以被暗示,我们不需要指定它,只是假装根本没有泛型。

有点离题,但我想知道你是否真的希望这个方法接受任何类型的对象。使用指定您可能接受的每种类型的通用约束可能会更好。或者也许是一个接口。在此处阅读通用约束:https://msdn.microsoft.com/en-us/library/d5x73970.aspx

现在您确实询问了如何在没有重载的情况下执行此操作,所以我也会添加它,但不推荐。

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;
}

【讨论】:

  • 有趣的建议。尽管仍然需要重载,但至少只剩下 2 个了。
  • 重载不是必需的,因为使用泛型您可以使用 hexNumber is/as IFormattable 然后删除私有方法。但是我更喜欢重载方法,因为它从您的代码中删除了类型检查/强制转换。
  • 会是泛型方法没有重载,比对象方法更好吗?
  • 绝对!这让 .Net 可以确定在编译期间使用一次的 .ToString() 方法,而不是每次运行时使用。但是要了解实际发生的事情是 .Net 正在为您制造重载。每次调用具有新类型 (T) 的泛型时,编译器都会创建方法或类的非泛型版本并将其添加到输出中。这就是我们在 .Net 中获得从性能角度来看基本上没有问题的泛型的方式。
【解决方案2】:

我认为你最好对这样的事情使用正则表达式。

首先,Convert.ToString 已经按照您需要的方式格式化对象,因此您可以替换该代码。

为了将值转换为其字符串表示,该方法尝试 调用值的 IConvertible.ToString 实现。如果价值确实 未实现 IConvertible 接口,该方法尝试调用 IFormattable.ToString 实现值。如果值不 实现IFormattable接口,方法调用ToString 基础类型值的方法。

其次,当你需要一个字符串以某种方式显示时,正则表达式几乎总是要走的路。如果您编译它们,它们也非常快。由于这个原因,99% 的时间您都希望将正则表达式对象放在静态变量中。我使用Regexr 来测试正则表达式。它还有一个额外的好处是优雅地排除返回的字符串绝对不是十六进制字符串。

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 

【讨论】:

  • 不错的选择!谢谢!
猜你喜欢
  • 2018-06-18
  • 1970-01-01
  • 2019-07-04
  • 1970-01-01
  • 1970-01-01
  • 2023-02-01
  • 1970-01-01
  • 1970-01-01
  • 2016-01-25
相关资源
最近更新 更多