У меня есть метод как это:
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(ushort level, ushort capacity)
{
T percentage;
if(capacity == 1)
percentage = 1; // error here
// do calculations...
return percentage;
}
Это даже возможно? Я не был уверен, но я думал, что это сообщение могло бы предположить, что то, что я пытаюсь сделать, просто невозможно.
Править
Благодаря всем, кто ответил, много хороших ответов. Как Tomas указал, это, вероятно, лучше всего сделано в двух отдельных методах. Как указано и TreDubZedd и TcKs, лучший способ получить функциональность, которую я хотел бы, состоит в том, чтобы использовать неявное преобразование, которое могло возвратить или двойное или десятичное неявно.
На самом деле, вам нужны не дженерики, а перегрузка. Однако вам нужна перегрузка по типу возвращаемого значения, которая поддерживается IL, но не поддерживается C#.
Я предпочитаю два метода для каждого типа возвращаемого значения:
static double ComputePercentageDouble (ushort level, ushort capacity)
static decimal ComputePercentageDecimal (ushort level, ushort capacity)
Альтернативой может быть тип custome с неявными операторами приведения:
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;
}
}
Это некрасиво, но попробуйте:
percentage = (T)Convert.ChangeType(1, typeof(T));
Это работает как минимум для double
и decimal
.
Возможно, вы сможете использовать неявное преобразование: http://msdn.microsoft.com/en-us/library/zk2z37d3.aspx
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();
}
Почему бы не создать класс 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);
}
Обобщения полезны для написания кода, который работает с любыми типами (возможно, реализует какой-либо интерфейс, который можно указать с помощью , где
). Однако, если вы хотите использовать их для реализации метода, который может возвращать два разных числовых типа, это покажется немного неправильным (он будет работать, только если 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
в качестве типа параметр и использует его для выполнения всей математики внутри метода:
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>(...);
Это хороший трюк и, вероятно, лучший (типобезопасный) обходной путь, который вы можете получить. Однако это немного сложно, поэтому лучше объявить два отдельных метода.
Если вы используете C # 4.0, вы можете просто вернуть динамический
.