【问题标题】:Switch case and generics checking切换案例和泛型检查
【发布时间】:2012-03-21 10:06:37
【问题描述】:

我想编写一个函数,将intdecimal 以不同的方式格式化为字符串

我有这个代码:

我想将其重写为泛型:

    public static string FormatAsIntWithCommaSeperator(int value)
    {
        if (value == 0 || (value > -1 && value < 1))
            return "0";
        return String.Format("{0:#,###,###}", value);
    }

    public static string FormatAsDecimalWithCommaSeperator(decimal value)
    {
        return String.Format("{0:#,###,###.##}", value);
    }


    public static string FormatWithCommaSeperator<T>(T value) where T : struct
    {
        string formattedString = string.Empty;

        if (typeof(T) == typeof(int))
        {
            if ((int)value == 0 || (value > -1 && value < 1))
            return "0";

            formattedString = String.Format("{0:#,###,###}", value);
        }

        //some code...
    }

    /// <summary>
    /// If the number is an int - returned format is without decimal digits
    /// </summary>
    /// <param name="value"></param>
    /// <returns></returns>
    public static string FormatNumberTwoDecimalDigitOrInt(decimal value)
    {
        return (value == (int)value) ? FormatAsIntWithCommaSeperator(Convert.ToInt32(value)) : FormatAsDecimalWithCommaSeperator(value);
    }

如何在函数体中使用 T?

我应该使用什么语法?

【问题讨论】:

  • 为什么不只有两个重载?我猜这是有原因的,但从你的例子来看,我宁愿有两种方法而不是打开类型。
  • 为什么在这里使用泛型?您的方法是否可用于任何结构,即使是我自己定义的结构?
  • 泛型在这里没有用,您不妨使用 object 作为参数类型。注意代码实际上是一样的。
  • 如果值没有小数部分,您是否只想省略小数位?

标签: c# generics


【解决方案1】:

您可以使用TypeCode enum 进行切换:

switch (Type.GetTypeCode(typeof(T)))
{
    case TypeCode.Int32:
       ...
       break;
    case TypeCode.Decimal:
       ...
       break;
}

从 C# 7.0 开始你可以使用pattern matching:

switch (obj)
{
    case int i:
       ...
       break;
    case decimal d:
       ...
       break;
    case UserDefinedType u:
       ...
       break;
}

从 C# 8.0 开始,您可以使用 switch expressions:

string result = obj switch {
    int i => $"Integer {i}",
    decimal d => $"Decimal {d}",
    UserDefinedType u => "User defined {u}",
    _ => "unexpected type"
};

【讨论】:

  • 用户类型呢?
【解决方案2】:

在现代 C# 中:

public static string FormatWithCommaSeperator<T>(T value) where T : struct
{
    switch (value)
    {
        case int i:
            return $"integer {i}";
        case double d:
            return $"double {d}";
    }
}

【讨论】:

  • 如果我只得到 T 而不是 T 值会怎样?说 public T Get(字符串名称){ }
  • 你可以使用default(T)。但我不确定这是否比只有一个带有键类型的字典更快。如果您想要的是返回不同类型的键控集合,只需将该值保留为公共基本类型(或 Object),然后直接在 get 中进行强制转换,或者使用返回 null 的as。这是完美的行为,进行类型检查不会为您节省任何代码,只会使错误处理复杂化。
  • 更正我自己,default(T) 解决方案不适用于 ref 类型,因为默认值为 null。所以你只需要测试类型,或者可能只是找到更好的设计。
【解决方案3】:

另一种开启泛型的方法是:

switch (typeof(T))
{
    case Type intType when intType == typeof(int):
        ...
    case Type decimalType when decimalType == typeof(decimal):
        ...
    default:
        ...
}

注意when as a case guard in switch expressions was introduced in C# 7.0/Visual Studio 2017。

【讨论】:

    【解决方案4】:

    我有一个类似的问题,但使用的是自定义类而不是内置数据类型。以下是我的做法:

    switch (typeof(T).Name)
    {
        case nameof(Int32):
            break;
        case nameof(Decimal):
            break;
    }
    

    我修改它以使用您正在使用的类型(即 int 和 decimal)。比起硬编码字符串,我更喜欢这种方法,因为重构类名不会破坏这段代码。

    对于较新版本的 C#,您有时也可以这样做:

    switch (Activator.CreateInstance(typeof(T)))
    {
        case int _:
            break;
        case decimal _:
            break;
    }
    

    我说“有时”是因为这仅适用于具有默认构造函数的类型。这种方法使用模式匹配和丢弃。我不太喜欢它,因为您需要创建对象的实例(然后将其丢弃)并且因为默认的构造函数要求。

    【讨论】:

      【解决方案5】:

      编辑:如果您只想精确处理 int 和 double,只需有两个重载:

      DoFormat(int value)
      {
      }
      
      DoFormat(double value)
      {
      }
      

      如果你坚持使用泛型:

      switch (value.GetType().Name)
      {
          case "Int32":
              break;
          case "Double":
              break;
          default:
              break;
      }
      

      if (value is int)
      {
          int iValue = (int)(object)value;
      }
      else if (value is double)
      {
          double dValue = (double)(object)value;
      }
      else
      {
      }
      

      【讨论】:

      • 你是对的。我更新了帖子(添加了对象转换)。无论如何,我更喜欢 Nikola 的答案——它更具可读性。
      • public T myMethod&lt;T&gt;(T value) where T : object 也可以,不需要演员表!!
      【解决方案6】:

      打开泛型的更多格式化方式是:

      switch (true)
      {
          case true when typeof(T) == typeof(int):
              ...
          case true when typeof(T) == typeof(decimal):
              ...
          default:
              ...
      }
      

      【讨论】:

        【解决方案7】:

        或者你总是可以这样做:

        public static string FormatWithCommaSeparator<T>(T[] items)
        {
            var itemArray = items.Select(i => i.ToString());
        
            return string.Join(", ", itemArray);
        }
        

        【讨论】:

          【解决方案8】:

          你可以检查变量的类型;

              public static string FormatWithCommaSeperator<T>(T value)
              {
                  if (value is int)
                  {
                      // Do your int formatting here
                  }
                  else if (value is decimal)
                  {
                      // Do your decimal formatting here
                  }
                  return "Parameter 'value' is not an integer or decimal"; // Or throw an exception of some kind?
              }
          

          【讨论】:

            【解决方案9】:

            您可以使用 IConvertible 代替泛型

                public static string FormatWithCommaSeperator(IConvertible value)
                {
                        IConvertible convertable = value as IConvertible;
                        if(value is int)
                        {
                            int iValue = convertable.ToInt32(null);
                            //Return with format.
                        }
                        .....
                }
            

            【讨论】:

            • IConvertible convertable = value as IConvertible; 这行对我来说是多余的。
            【解决方案10】:

            C# 8 中可以使用(将“...”替换为相关代码):

            ... type switch
            {
                Type _ when type == typeof(int) => ...,
                Type _ when type == typeof(decimal) => ...,
                _ => ... // default case
            };
            

            另一个优雅的选择(将“...”替换为相关代码):

            ... Type.GetTypeCode(type) switch
            {
                TypeCode.Int32 => ...,
                TypeCode.Decimal => ...,
                _ => ...
            };
            

            更多信息: https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/operators/switch-expression

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2016-10-10
              • 1970-01-01
              • 2010-11-02
              相关资源
              最近更新 更多