简体   繁体   中英

Switch case and generics checking

I want to write a function that format int and decimal differently into string

I have this code:

and I want to rewrite it to generics:

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

How can i use T in the function body?

What syntax should I use?

You might use TypeCode for switch:

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

In modern 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}";
    }
}

Another way to do switch on generic is:

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

Note that when as a case guard in switch expressions was introduced in C# 7.0 /Visual Studio 2017.

Edit: If you only want to handle exactly int and double, just have two overloads:

DoFormat(int value)
{
}

DoFormat(double value)
{
}

If you insist on using generics:

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

OR

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

I had a similar question, but with custom classes rather than built-in data types. Here's how I went about it:

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

I modified it to use the types you are using (ie, int and decimal). I like this approach more than hard coding strings, as a refactor of a class name will not break this code.

With newer versions of C#, you could also do this some of the time:

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

I say "some of the time" because that would only really work with types that have a default constructor. This approach uses pattern matching and discards. I don't really like it since you need to create an instance of the object (that you then throw away) and because of the default constructor requirement.

more formatted way to do switch on generic is:

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

In C# 8 you can use (replace "..." with the relevant code):

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

Another elegant option (replace "..." with the relevant code):

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

For more info: https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/operators/switch-expression

Alternatively you could always do:

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

    return string.Join(", ", itemArray);
}

You could check the type of the variabele;

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

You could instead of using generics use IConvertible

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM