开发者

How to create a generic C# method that can return either double or decimal?

I have a method like this:

private static double ComputePercentage(ushort level, ushort capacity)
{
      double percentage;
      if(capacity == 1)
        percentage = 1;

      // do calculations...
      return percentage;
}

Is it possible to make it of a generic type like "type T" where it can return either decimal or double, depending on the type of method expected (or the type put into the function?)

I tried something like this and I couldn't get it to work, because I cannot assign a number like "1" to a generic开发者_运维问答 type. I also tried using the "where T :" after ushort capacity) but I still couldn't figure it out.

private static T ComputePercentage<T>(ushort level, ushort capacity)
{
      T percentage;
      if(capacity == 1)
        percentage = 1; // error here

      // do calculations...
      return percentage;
}

Is this even possible? I wasn't sure, but I thought this post might suggest that what I'm trying to do is just plain impossible.


EDIT

Thanks to all who responded, many good answers. As Tomas pointed out, this is probably best done in two separate methods. As pointed out by both TreDubZedd and TcKs, the best way to get the functionality I would want is to use an implicit conversion that could return either a double or decimal implicitly.


In fact, you don't need generics but overloading. However you need overloading by return value's type which is supported by IL but is not supported by C#.

I prefere two methods for every return's value type:

static double  ComputePercentageDouble  (ushort level, ushort capacity)
static decimal ComputePercentageDecimal (ushort level, ushort capacity)

The alternative can be custome type with implicit cast operators:

decimal decimalPercentage = ComputePercentage( 1, 2 );
double  doublePercentage  = ComputePercentage( 1, 2 );

static DoubleDecimal ComputePercentage( ushort level, ushort capacity ) {
    DoubleDecimal percentage = default( DoubleDecimal );
    if ( capacity == 1 )
        percentage.Number = 1; // error here

    // do calculations...
    return percentage;
}


public struct DoubleDecimal {
    public decimal Number;

    public static implicit operator decimal( DoubleDecimal value ) {
        return value.Number;
    }
    public static implicit operator double( DoubleDecimal value ) {
        return (double)value.Number;
    }
}


You might be able to use implicit conversion: http://msdn.microsoft.com/en-us/library/zk2z37d3.aspx


Generics are useful for writing code that works with any types (perhaps implementing some interface, which can be specified using where). However, if you want to use them for implementing method that can return two different numeric types, it feels a bit wrong (it would work only if double and decimal implemented some shared interface).

You should probably define two distinct methods (e.g. ComputePercentage and ComputePercentagePrecise or something like that - since you can't use overloading using different parameters).

It may be possible to workaround this limitation by using something like this (but that's probably overly complicated for you):

class INumericOps<T> {
  public abstract T One { get; }
  public abstract T Add(T a, T b); 
  // and other operations that you may need
}

// Implementations of numeric operations for decimal and double
class DecimalNumerics : INumericOps<decimal> { ... }
class DoubleNumerics : INumericOps<double> { ... }

Then you would write a method that takes INumericOps<T> as a type parameter and uses it to do all mathematics inside the method:

private static R ComputePercentage<T, R>(ushort level, ushort capacity) 
  where T : INumericOps<R>, where T : new() {
  INumericOps<R> ops = new T(); // Get instance with numeric operations
  T percentage;  
  if(capacity == 1)  
    percentage = ops.One; 

  // Calculations using ops.Add(.., ..) instead of + etc.
  return percentage;  
}  

Then you would call it like this:

decimal res = ComputePercentage<DecimalNumerics, decimal>(...);

This is a nice trick and it is probably the best (type-safe) workaround you can get. However, it is a bit complicated, so declaring two separate methods may be a better idea.


It's not pretty, but try:

percentage = (T)Convert.ChangeType(1, typeof(T));

This works for both double and decimal at least.


If you're using C# 4.0 you could just return a dynamic.


private static T ComputePercentage<T>(ushort level, ushort capacity)
{
      if (typeof(T) == typeof(decimal))
        {
            decimal percentage = 1;
            return (T) (object) percentage;
        }

        if (typeof(T) == typeof(double))
        {
            double percentage = 1;
            return (T) (object) percentage;
        }

      throw new NotSupportedExcpetion();
}


Why not create a Percent class?

class Percent
{
    public Percent(double value)
    {
        this.value = value;
    }

    public double AsDouble()
    {
        return value;
    }

    public decimal AsDecimal()
    {
        return (decimal)value;
    }

    readonly double value;
}

static Percent ComputePercentage(ushort level, ushort capacity)
{
    double percentage = 0;
    if (capacity == 1)
    {
        percentage = 1;
    }

    // calculations

    return new Percent(percentage);
}
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜