我有一个数字列表,我写了一个方法,对这些数字进行一些计算;总而言之,它是关于一页代码的。该方法对这些数字进行了一些算术和比较。
我的问题是,在一种情况下,列表是IList<byte>
,而在另一种情况下,它是IList<float>
。两种情况下的算法完全相同(是的,我知道溢出错误和精度损失等问题,但在我的情况下它可以工作)。如何编写一个可以处理这两个列表的方法?我不能写像void DoStuff<T>(IList<T> numbers)
这样的东西,因为没有通用的算术运算符(+ - * /
)。
一种解决方案是简单地将所有内容存储为浮动,但我想避免它。列表很长,因此存储浮点数而不是字节会花费太多内存。我也可以做DoStuffFloat(byteList.Select(b => (float)b))
这样的事情,但我也不想支付性能损失,如果我能避免的话。
没有复制粘贴整个方法并将“float”替换为“byte”(反之亦然),是否有一些不错的解决方案?
编辑:我应该提到我只限于在这个项目中使用.NET 3.5。
答案 0 :(得分:5)
我不知道这是否是针对您案例的最佳方法,但对类似案例也很有用。
可以使用dynamic
关键字来完成此操作。动态做什么是它不会在运行时之前进行编译时检查。
这是一个小样本程序,展示它的工作原理。
class Program
{
static void Main()
{
List<byte> bytes = new List<byte>();
bytes.Add(2);
bytes.Add(1);
List<float> floats = new List<float>();
floats.Add(2.5F);
floats.Add(1F);
Console.WriteLine(DoStuff(bytes));
Console.WriteLine(DoStuff(floats));
Console.ReadLine();
}
static dynamic DoStuff(IList items)
{
dynamic item0 = items[0];
dynamic item1 = items[1];
return item0 - item1;
}
}
不幸的是,在我的快速测试中,我无法使IList<dynamic> work
使用非通用IList
,然后以dynamic
方式访问成员。
答案 1 :(得分:5)
您可以做的是创建一个通用接口,其中包含您要支持的操作,创建一个通用工厂来为受支持的类型创建实例以执行操作,并使用它。
如,
public interface IOperations<T>
{
T Add(T a, T b);
T Subtract(T a, T b);
T Multiply(T a, T b);
T Divide(T a, T b);
}
public static class Operations<T>
{
public static IOperations<T> Default { get { return Create(); } }
static IOperations<T> Create()
{
var type = typeof(T);
switch (Type.GetTypeCode(type))
{
case TypeCode.Byte:
return (IOperations<T>)new ByteOperations();
case TypeCode.Single:
return (IOperations<T>)new SingleOperations();
default:
var message = String.Format("Operations for type {0} is not supported.", type.Name);
throw new NotSupportedException(message);
}
}
class ByteOperations : IOperations<byte>
{
public byte Add(byte a, byte b) { return unchecked ((byte)(a + b)); }
public byte Subtract(byte a, byte b) { return unchecked ((byte)(a - b)); }
public byte Multiply(byte a, byte b) { return unchecked ((byte)(a * b)); }
public byte Divide(byte a, byte b) { return unchecked ((byte)(a / b)); }
}
class SingleOperations : IOperations<float>
{
public float Add(float a, float b) { return a + b; }
public float Subtract(float a, float b) { return a - b; }
public float Multiply(float a, float b) { return a * b; }
public float Divide(float a, float b) { return a / b; }
}
}
T Mean<T>(IList<T> numbers)
{
var operations = Operations<T>.Default;
var sum = numbers.Aggregate(operations.Add);
var count = (T)Convert.ChangeType(numbers.Count, typeof(T));
return operations.Divide(sum, count);
}
var resultByte = Mean(new byte[] { 1, 2, 3, 4 }); // 2
var resultSingle = Mean(new float[] { 1.1F, 2.1F, 3.1F, 4.1F }); // 2.6F
var resultInt = Mean(new int[] { 1, 2, 3, 4 }); // not supported
如果您不介意小的性能影响,您可以动态创建所需的操作。
class GenericOperations<T> : IOperations<T>
{
public GenericOperations()
{
add = CreateLambda(Expression.Add);
subtract = CreateLambda(Expression.Subtract);
multiply = CreateLambda(Expression.Multiply);
divide = CreateLambda(Expression.Divide);
}
private Func<T, T, T> add, subtract, multiply, divide;
private static Func<T, T, T> CreateLambda(Func<Expression, Expression, BinaryExpression> op)
{
var a = Expression.Parameter(typeof(T), "a");
var b = Expression.Parameter(typeof(T), "b");
var body = op(a, b);
var expr = Expression.Lambda<Func<T, T, T>>(body, a, b);
return expr.Compile();
}
public T Add(T a, T b) { return add(a, b); }
public T Subtract(T a, T b) { return subtract(a, b); }
public T Multiply(T a, T b) { return multiply(a, b); }
public T Divide(T a, T b) { return divide(a, b); }
}
答案 2 :(得分:0)
创建用于包装基础值的类,并让它们各自实现一个包含所需操作的接口。然后,使用该接口的IList
代替原始值。