Как создать универсальный метод C #, который может возвращать либо двойной, либо десятичный? - PullRequest
6 голосов
/ 28 мая 2010

У меня есть такой метод:

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

      // do calculations...
      return percentage;
}

Можно ли сделать его универсального типа, такого как "тип T", где он может возвращать либо десятичное, либо двойное в зависимости от ожидаемого типа метода (или типа, введенного в функцию?)

Я попробовал что-то подобное и не смог заставить его работать, потому что я не могу присвоить число типа «1» универсальному типу. Я также пытался использовать «где T:» после ushort capacity), но я все еще не мог понять это.

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

      // do calculations...
      return percentage;
}

Это вообще возможно? Я не был уверен, но я думал, что этот пост может предположить, что то, что я пытаюсь сделать, просто невозможно.


EDIT

Спасибо всем, кто ответил, много хороших ответов. Как указал Томас , это, вероятно, лучше всего сделать двумя отдельными методами. Как указывалось в TreDubZedd и TcKs , лучший способ получить желаемую функциональность - это использовать неявное преобразование, которое могло бы неявно возвращать двойное или десятичное значение.

Ответы [ 7 ]

5 голосов
/ 28 мая 2010

На самом деле вам не нужны дженерики, кроме перегрузки. Однако вам нужна перегрузка по типу возвращаемого значения, которое поддерживается IL, но не поддерживается C #.

Я предпочитаю два метода для каждого типа возвращаемого значения:

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

Альтернативой может быть обычный тип с неявными операторами приведения:

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;
    }
}
3 голосов
/ 28 мая 2010

Обобщения полезны для написания кода, который работает с любыми типами (возможно, реализует некоторый интерфейс, который можно указать с помощью where). Однако, если вы хотите использовать их для реализации метода, который может возвращать два разных числовых типа, он будет немного ошибочным (он будет работать только в том случае, если double и decimal реализуют некоторый общий интерфейс).

Вам, вероятно, следует определить два разных метода (например, ComputePercentage и ComputePercentagePrecise или что-то в этом роде - поскольку вы не можете использовать перегрузку с использованием других параметров).

Может быть возможно обойти это ограничение, используя что-то вроде этого (но это, вероятно, слишком сложно для вас):

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> { ... }

Тогда вы бы написали метод, который принимает INumericOps<T> в качестве параметра типа и использует его для выполнения всей математики внутри метода:

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

Тогда вы бы назвали это так:

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

Это хороший трюк и, вероятно, лучший (безопасный для типов) обходной путь, который вы можете получить. Однако это немного сложно, поэтому лучше объявить два отдельных метода.

3 голосов
/ 28 мая 2010

Возможно, вы сможете использовать неявное преобразование: http://msdn.microsoft.com/en-us/library/zk2z37d3.aspx

1 голос
/ 28 мая 2010

Это не красиво, но попробуйте:

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

Это работает как минимум для double и decimal.

0 голосов
/ 28 мая 2010

Почему бы не создать класс Percent?

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 голосов
/ 28 мая 2010

Если вы используете C # 4.0, вы можете просто вернуть dynamic.

0 голосов
/ 28 мая 2010
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();
}
...