4

数値のリストがあり、これらの数値に対していくつかの計算を実行するメソッドを作成しました。全体として、これは 1 ページのコードに関するものです。このメソッドは、これらの数値に対していくつかの算術演算と比較を実行します。

私の問題は、ある場合にはリストがIList<byte>であり、別の場合にはIList<float>. どちらの場合もアルゴリズムはまったく同じです (はい、オーバーフロー エラーや精度の低下などは認識していますが、私の場合は機能します)。両方のリストを処理するメソッドを作成するにはどうすればよいですか? DoStuff<T>(IList<T> numbers)汎用的な算術演算子 ( ) がないため、void のようなものを書くことはできません+ - * /

1 つの解決策は、単純にすべてを float として格納することですが、それは避けたいと思います。リストは非常に長いため、バイトの代わりにフロートを格納すると、メモリが大量に消費されます。のようなこともできますがDoStuffFloat(byteList.Select(b => (float)b))、回避できるのであれば、パフォーマンスのペナルティも払いたくありません。

メソッド全体をコピーして貼り付け、「float」を「byte」に置き換える(またはその逆)以外に、適切な解決策はありますか?

編集:このプロジェクトでは.NET 3.5の使用に制限されていることを言及する必要がありました。

4

3 に答える 3

6

あなたができることは、サポートしたい操作を含むジェネリック インターフェイスを作成し、ジェネリック ファクトリを作成して、操作を実行するサポートされている型のインスタンスを作成し、それを使用することです。

例えば、

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); }
}
于 2012-09-13T22:54:44.350 に答える
5

I don't know if this is the best method for your case but it is useful for similar cases too.

This can be done by using the dynamic keyword. What dynamic will do is it will not do the compile time checks until runtime.

Here is a small sample program to show how it works.

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

}

Unfortunately in my quick testing I could not make IList<dynamic> work however using the non generic IList then accessing the members as a dynamic works fine.

于 2012-09-13T22:30:12.623 に答える
0

基礎となる値をラップするクラスを作成し、それぞれに必要な操作を備えたインターフェースを実装させます。次に、IList生の値の代わりにそのインターフェイスの を使用します。

于 2012-09-13T22:36:36.983 に答える