是否可以专门化泛型

时间:2011-07-20 09:41:40

标签: c# generics casting

我有一个通用的Converter类

public class Converter
{
       public static TargetType[] Convert<SourceType, TargetType>(SourceType[] data)
            where SourceType : struct
            where TargetType : struct
        {
            int N = data.Length;
            TargetType[] result = new TargetType[N];

            for (int i = 0; i < N; i++)
            {
                // We does this not work, what can I do else
                result[i] = (TargetType)data[i];
            }

            return result;
        }
}

问题1:为什么不能进行演员表演?我还能做什么? Convert.ChangeType要慢。

由于性能较差,我决定在不安全的代码中执行此操作。不幸的是,它不可能创建指向泛型的指针 - 因为它是众所周知的。出于这个原因,我创建了一个转换器,它在运行时决定类型并使用大量if来执行转换。

没有人提出任何问题。我可以创建一个通用的Convert例程并以某种方式对其进行特殊处理,在某些情况下我已经定义了数据类型。我目前不知道怎么做,但也许我看了你得到线索的例子:

public static double[] Convert<double, int>(int[] data)
        {
            int N = data.Length;
            double[] result = new double[N];

            unsafe
            {
                fixed (int* data_pinned = data)
                fixed (double* out_pinned = result)
                {
                    int* A = data_pinned;
                    double* B = out_pinned;

                    for (int i = 0; i < N; i++, A++, B++)
                    {
                        *B = ((double)*A);
                    }
                }
            }

            return result;
        }

下面你可以找到一个“小”的演示项目,它显示了不同演员的表现值:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;

namespace ConsoleApplication1
{
    public static class CastTest
    {
        public static unsafe double[] Cast1(int[] input)
        {
            int N = input.Length;
            double[] output = new double[N];

            for (int i = 0; i < N; i++) output[i] = (double)(input[i]);

            return output;
        }

        public static unsafe double[] Cast2(int[] input)
        {
            int N = input.Length;
            double[] output = new double[N];

            fixed (double* output_pinned = output)
            {
                double* outp = output_pinned;

                fixed (int* input_pinned = input)
                {
                    int* inp = input_pinned;

                    for (int i = 0; i < N; i++, inp++, outp++) *outp = (double)(*inp);
                }

                return output;
            }
        }

        public static unsafe double[] Cast3(int[] input)
        {
            int N = input.Length;
            double[] output = new double[N];

            fixed (double* output_pinned = output)
            {
                double* outp = output_pinned;

                fixed (int* input_pinned = input)
                {
                    int* inp = input_pinned;

                    for (int i = 0; i < N; i++) outp[i] = (double)(inp[i]);
                }

                return output;
            }
        }

        public static unsafe double[] Cast4(int[] input)
        {
            int N = input.Length;
            double[] output = new double[N];
            fixed (double* output_pinned = output)
            {
                fixed (int* input_pinned = input)
                {
                    for (int i = 0; i < N; i++) output_pinned[i] = (double)(input_pinned[i]);
                }
            }

            return output;
        }

        public static unsafe double[] Cast5(int[] input)
        {
            double[] output = new double[input.Length];

            for (int i = 0; i < input.Length; i++) output[i] = (input[i]);

            return output;
        }

        public static unsafe double[] Cast6(int[] input)
        {
            Converter<int, double> converter = (A) =>
                {
                    return A;
                };

            return Array.ConvertAll<int, double>(input, converter);
        }

        public static void StartTest()
        {
            int[] A = new int[100000];
            int N = 10000;

            var w1 = Stopwatch.StartNew();
            for (int i = 0; i < N; i++) { double[] A1 = Cast1(A); }
            w1.Stop();
            Console.WriteLine(String.Format("{0}: {1}ms", 1, w1.ElapsedMilliseconds));

            var w2 = Stopwatch.StartNew();
            for (int i = 0; i < N; i++) { double[] A2 = Cast2(A); }
            w2.Stop();
            Console.WriteLine(String.Format("{0}: {1}ms", 2, w2.ElapsedMilliseconds ));

            var w3 = Stopwatch.StartNew();
            for (int i = 0; i < N; i++) { double[] A3 = Cast3(A); }
            w3.Stop();
            Console.WriteLine(String.Format("{0}: {1}ms", 3, w3.ElapsedMilliseconds));

            var w4 = Stopwatch.StartNew();
            for (int i = 0; i < N; i++) { double[] A4 = Cast4(A); }
            w4.Stop();
            Console.WriteLine(String.Format("{0}: {1}ms", 4, w4.ElapsedMilliseconds));

            var w5 = Stopwatch.StartNew();
            for (int i = 0; i < N; i++) { double[] A5 = Cast5(A); }
            w5.Stop();
            Console.WriteLine(String.Format("{0}: {1}ms", 5, w5.ElapsedMilliseconds));

            var w6 = Stopwatch.StartNew();
            for (int i = 0; i < N; i++) { double[] A6 = Cast6(A); }
            w6.Stop();
            Console.WriteLine(String.Format("{0}: {1}ms", 6, w6.ElapsedMilliseconds));
        }
    }
}

非常感谢任何帮助以提高性能。 数组大小和重复的大小对我来说都是实际值。实际上,出于测试目的,这些值也比通常使用的值略低。

有可能这样做吗?

由于 马丁

1 个答案:

答案 0 :(得分:2)

无法进行演员表,因为无法保证 在所涉及的两种类型之间进行转换。如果SourceTypeGuidTargetTypedouble,该怎么办?编译器基本上不能发出转换。

最简单的方法可能是提供代表:

public static TTarget[] Convert<TSource, TTarget>(TSource[] data,
    Func<TSource, TTarget> conversion)
{
    TargetType[] result = new TargetType[data.Length];

    for (int i = 0; i < data.Length; i++)
    {
        result[i] = conversion(data[i]);
    }

    return result;
}

当然这与调用Array.ConvertAll基本相同......那不能做你想要的吗?

是的,经常调用转换会产生很小的性能开销 - 但是你是否证明了自己的性能开销是个问题?我至少尝试使用Array.ConvertAll作为第一次尝试。