通用扩展方法类型推断
Generic extension method type inference
我有一个扩展方法,可以将数值类型 A 的数组转换为数值类型 B 的数组
public static T2[] ConvertTo<T1, T2>(this T1[] buffer)
{
var bufferNumBytes = buffer.Length * Marshal.SizeOf(default(T1));
var targetElementNumBytes = Marshal.SizeOf(default(T2));
if (bufferNumBytes % targetElementNumBytes != 0)
{
throw new ArgumentException($"Array must have multiple of {targetElementNumBytes} bytes, has {bufferNumBytes} bytes instead", nameof(buffer));
}
var res = new T2[bufferNumBytes / targetElementNumBytes];
Buffer.BlockCopy(buffer, 0, res, 0, bufferNumBytes);
return res;
}
我可以这样称呼它
var b = new ushort[] { 1, 256 };
var us = b.ConvertTo<ushort, byte>();
Assert.That(us[0], Is.EqualTo(1));
Assert.That(us[1], Is.EqualTo(0));
Assert.That(us[2], Is.EqualTo(0));
Assert.That(us[3], Is.EqualTo(1));
然而,T1 参数似乎是多余的,但我不知道如何摆脱它。
一种解决方法是更通用的扩展方法
public static T1[] ConvertTo<T1>(this byte[] buffer)
{
return buffer.ConvertTo<byte, T1>();
}
public static T1[] ConvertTo<T1>(this sbyte[] buffer)
{
return buffer.ConvertTo<sbyte, T1>();
}
public static T1[] ConvertTo<T1>(this ushort[] buffer)
{
return buffer.ConvertTo<ushort, T1>();
}
...
但我更喜欢一种更简单的方法,只有一个参数(目标类型)并且编译器自己推断缓冲区的类型。
您可以使用流畅的界面获得大部分所需内容:
void Main()
{
var b = new ushort[] { 1, 256 };
// We now use a fluent interface to do the conversion - T1 is
// now inferred.
var us = b.Convert().To<byte>();
us.Dump();
}
public static class Extensions
{
public static IConverter Convert<T1>(this T1[] buffer)
{
return new Converter<T1>(buffer);
}
}
public interface IConverter
{
T2[] To<T2>();
}
public class Converter<T1> : IConverter
{
private readonly T1[] _buffer;
public Converter(T1[] buffer)
{
_buffer = buffer ?? throw new ArgumentNullException();
}
public T2[] To<T2>()
{
var bufferNumBytes = _buffer.Length * Marshal.SizeOf(default(T1));
var targetElementNumBytes = Marshal.SizeOf(default(T2));
if (bufferNumBytes % targetElementNumBytes != 0)
{
throw new ArgumentException($"Array must have multiple of {targetElementNumBytes} bytes, has {bufferNumBytes} bytes instead", nameof(_buffer));
}
var res = new T2[bufferNumBytes / targetElementNumBytes];
Buffer.BlockCopy(_buffer, 0, res, 0, bufferNumBytes);
return res;
}
}
缺点是您正在为 Converter
class 分配额外的内存,并且您最终调用了两个方法 - 但可发现性基本上应该与您的原始示例一样好,这就是我思考真的激发了你的问题。
我有一个扩展方法,可以将数值类型 A 的数组转换为数值类型 B 的数组
public static T2[] ConvertTo<T1, T2>(this T1[] buffer)
{
var bufferNumBytes = buffer.Length * Marshal.SizeOf(default(T1));
var targetElementNumBytes = Marshal.SizeOf(default(T2));
if (bufferNumBytes % targetElementNumBytes != 0)
{
throw new ArgumentException($"Array must have multiple of {targetElementNumBytes} bytes, has {bufferNumBytes} bytes instead", nameof(buffer));
}
var res = new T2[bufferNumBytes / targetElementNumBytes];
Buffer.BlockCopy(buffer, 0, res, 0, bufferNumBytes);
return res;
}
我可以这样称呼它
var b = new ushort[] { 1, 256 };
var us = b.ConvertTo<ushort, byte>();
Assert.That(us[0], Is.EqualTo(1));
Assert.That(us[1], Is.EqualTo(0));
Assert.That(us[2], Is.EqualTo(0));
Assert.That(us[3], Is.EqualTo(1));
然而,T1 参数似乎是多余的,但我不知道如何摆脱它。 一种解决方法是更通用的扩展方法
public static T1[] ConvertTo<T1>(this byte[] buffer)
{
return buffer.ConvertTo<byte, T1>();
}
public static T1[] ConvertTo<T1>(this sbyte[] buffer)
{
return buffer.ConvertTo<sbyte, T1>();
}
public static T1[] ConvertTo<T1>(this ushort[] buffer)
{
return buffer.ConvertTo<ushort, T1>();
}
...
但我更喜欢一种更简单的方法,只有一个参数(目标类型)并且编译器自己推断缓冲区的类型。
您可以使用流畅的界面获得大部分所需内容:
void Main()
{
var b = new ushort[] { 1, 256 };
// We now use a fluent interface to do the conversion - T1 is
// now inferred.
var us = b.Convert().To<byte>();
us.Dump();
}
public static class Extensions
{
public static IConverter Convert<T1>(this T1[] buffer)
{
return new Converter<T1>(buffer);
}
}
public interface IConverter
{
T2[] To<T2>();
}
public class Converter<T1> : IConverter
{
private readonly T1[] _buffer;
public Converter(T1[] buffer)
{
_buffer = buffer ?? throw new ArgumentNullException();
}
public T2[] To<T2>()
{
var bufferNumBytes = _buffer.Length * Marshal.SizeOf(default(T1));
var targetElementNumBytes = Marshal.SizeOf(default(T2));
if (bufferNumBytes % targetElementNumBytes != 0)
{
throw new ArgumentException($"Array must have multiple of {targetElementNumBytes} bytes, has {bufferNumBytes} bytes instead", nameof(_buffer));
}
var res = new T2[bufferNumBytes / targetElementNumBytes];
Buffer.BlockCopy(_buffer, 0, res, 0, bufferNumBytes);
return res;
}
}
缺点是您正在为 Converter
class 分配额外的内存,并且您最终调用了两个方法 - 但可发现性基本上应该与您的原始示例一样好,这就是我思考真的激发了你的问题。