[英]How to cast a generic array into another type?
我仍然试图找到一种快速的方法来如何将TOutput类型的通用数组转换为另一个类型为TInput的数组。 我的所有数组都是数字数据类型,但由于C#对经常请求的数字没有类型约束,所以我目前不得不忍受这种约束。 建议的方法,比如之前投射到一个物体,似乎极大地减慢了我的演员阵容。 目前我有一个大的if / else构造,它检查一个类型并使用指针算法强制转换为已定义的类型,但这是为了将来处理大的方法。 Parallel.For似乎是一个摆脱指针和加快速度的好方法,但C#泛型约束似乎仍然存在问题,但下面代码中的Tout仍然是一个问题。 这是我的代码:
public static OutputType[] Cast<InputType, OutputType>(InputType[] inputArray_in)
{
var aRange = Partitioner.Create(0, inputArray_in.Length);
OutputType[] aResult = new OutputType[inputArray_in.Length];
Parallel.ForEach(aRange, (r) =>
{
for (int i = r.Item1; i < r.Item2; i++)
{
aResult[i] = (OutputType)(inputArray_in[i]);
}
});
return aResult;
}
例:
float[] A = { 0.1f, 0.2f, 0.6f };
int []B = Cast<float, int>(A);
在所有情况下,我的数组类型都是数值(float,short,double,...),并且大多数情况下,数组大约是512x512个图像,但是在一个卷中大约1000个切片的堆栈中。 你有没有机会有一个简单的方法来执行此操作?
测试代码
public static class CastTest
{
delegate double[] CastMethod(int[] input);
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)
{
return Array.ConvertAll<int, double>(input, x => (double)x);
}
public static double[] Cast6(int[] input)
{
var aRange = Partitioner.Create(0, input.Length);
int N = input.Length;
double[] output = new double[N];
Parallel.ForEach(aRange, (r) =>
{
for (int i = r.Item1; i < r.Item2; i++) output[i] = (double)(input[i]);
});
return output;
}
public unsafe static double[] Cast7(int[] input)
{
var aRange = Partitioner.Create(0, input.Length);
int N = input.Length;
double[] output = new double[N];
Parallel.ForEach(aRange, (r) =>
{
fixed (double* output_pinned = output)
{
double* outp = output_pinned + r.Item1;
fixed (int* input_pinned = input)
{
int* inp = input_pinned + r.Item1;
for (int i = r.Item1; i < r.Item2; i++, outp++, inp++) *outp = (double)(*inp);
}
}
});
return output;
}
public unsafe static double[] Cast8(int[] input)
{
var result = (from m in input.AsParallel() select (double)m).ToArray();
return result;
}
public static double[] Cast9(int[] input)
{
return (from m in input select (double)m).ToArray();
}
public static double[] Cast10(int[] input)
{
return (from m in input.AsParallel() select (double)m).ToArray();
}
public static double[] Cast11(int[] input)
{
return new List<double>(input.Select(p => (double)p)).ToArray();
}
static int[] A = new int[100000];
const int runs = 10000;
public static void StartTest()
{
TestMethod("1", Cast1);
TestMethod("2", Cast2);
TestMethod("3", Cast3);
TestMethod("4", Cast4);
TestMethod("5", Cast5);
TestMethod("6", Cast6);
TestMethod("7", Cast7);
TestMethod("8", Cast8);
TestMethod("9", Cast9);
TestMethod("10", Cast10);
TestMethod("11", Cast11);
}
static void TestMethod(string Name, CastMethod method)
{
var timer = Stopwatch.StartNew();
for (int i = 0; i < runs; i++) { double[] res = method(A); }
timer.Stop();
Console.WriteLine(String.Format("{0}: {1}ms", Name, timer.ElapsedMilliseconds));
}
}
谢谢马丁
像这样的数字类型之间没有魔术转换(当使用泛型等时); 有像Convert.ChangeType
或dynamic
技巧,但都涉及一个中间框/ unbox。
就个人而言,我只是在使用:
float[] A = { 0.1f, 0.2f, 0.6f };
int[] B = Array.ConvertAll(A, x => (int)x);
这将转换逻辑卸载到编译器(使用从float
到int
的正确转换,无需中间件或反射)。 但是,它在泛型内部不可用 - 即x => (OutputType)x
将不起作用。
你试过这个吗?
public static TOut[] Cast<TOut,TIn>(TIn[] arr) {
return arr.Select(x => (TOut)Convert.ChangeType(x,typeof(TOut))).ToArray();
}
为什么不使用简单的Cast
for lists,它是System.Linq.Enumerable
的LINQ Cast方法成员:
float[] A = { 0.1f, 0.2f, 0.6f };
int[] B = A.Cast(Of int).ToArray();
你可以在这里阅读: Enumerable.Cast(Of TResult)方法
我在浮点数组中进行了三次微不足道的实验,其中有38988个项目(我只是一遍又一遍地剪切和粘贴了一堆任意值)
//_a = array of floats
// pretty standard way of doing it 4ms
_result = (from m in _a select (int)m).ToArray();
// I was rather disappointed with this 35ms
_result = (from m in _a.AsParallel() select (int)m).ToArray();
// using a list rather surprised me 1ms
_result = new List<int>(_a.Select(p => (int)p)).ToArray();
所以我不知道这些与你的测试相比如何,但我会说选择通用列表非常有效。
编辑:
我正在添加我的代码。 我必须遗漏一些东西,因为与运行您的示例相比,我的示例得到了完全不同的结果。
class Program
{
static void Main(string[] args)
{
using (var x = new ArrayCast())
{
x.Run();
}
using (var x = new ArrayCastList())
{
x.Run();
}
using (var x = new ArrayCastAsParallel())
{
x.Run();
}
while (Console.Read() != 'q')
{
; // do nothing...
}
}
}
public abstract class Experiment : IAmATest, IDisposable
{
private Stopwatch _timer;
protected bool IgnoreAssert { get; set; }
#region IAmATest Members
public abstract void Arrange();
public abstract void Act();
public abstract void Assert();
#endregion
public void Run()
{
_timer = Stopwatch.StartNew();
Arrange();
_timer.Stop();
Console.WriteLine(String.Join(":", "Arrange", _timer.ElapsedMilliseconds));
_timer = Stopwatch.StartNew();
for (int i = 1; i < 1000; i++)
Act();
_timer.Stop();
Console.WriteLine(String.Join(":", "Act", _timer.ElapsedMilliseconds));
if (IgnoreAssert) { return; }
_timer = Stopwatch.StartNew();
Assert();
_timer.Stop();
Console.WriteLine(String.Join(":", "Assert", _timer.ElapsedMilliseconds));
}
public abstract void Dispose();
}
public class ArrayCast : Experiment
{
private int[] _a;
double[] _result;
public override void Arrange()
{
IgnoreAssert = true;
_a = new int[100000];
}
public override void Act()
{
_result = (from m in _a select (double)m).ToArray();
}
public override void Assert() { }
public override void Dispose() { _a = null; }
}
public class ArrayCastAsParallel : Experiment
{
private int[] _a;
double[] _result;
public override void Arrange()
{
IgnoreAssert = true;
_a = new int[100000];
}
public override void Act()
{
_result = (from m in _a.AsParallel() select (double)m).ToArray();
}
public override void Assert() { }
public override void Dispose() { _a = null; }
}
public class ArrayCastList : Experiment
{
private int[] _a;
double[] _result;
public override void Arrange()
{
IgnoreAssert = true;
_a = new int[100000];
}
public override void Act()
{
var x = new List<double>(_a.Select(p => (double)p));
}
public override void Assert() { }
public override void Dispose() { _a = null; }
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.