在我正在编写的程序中,我需要编写一个函数来获取任何数字类型(int,short,long等)并将其移入特定偏移量的字节数组。
存在一个Bitconverter.GetBytes()方法,该方法接受数字类型并将其作为字节数组返回,此方法仅采用数字类型。
到目前为止,我有:
private void AddToByteArray<T>(byte[] destination, int offset, T toAdd) where T : struct
{
Buffer.BlockCopy(BitConverter.GetBytes(toAdd), 0, destination, offset, sizeof(toAdd));
}
所以基本上我的目标是,例如,对AddToByteArray(数组,3,(短)10)的调用需要10并将其存储在数组的第4个插槽中。显式转换是存在的,因为我确切地知道我想要它占用多少字节。在某些情况下,我希望一个足够小的数字能够真正占用4个字节。另一方面,有时我想要将int压缩到一个字节。我这样做是为了创建一个自定义的网络数据包,如果这会让任何想法涌入你的脑袋。
如果泛型的where子句支持“where T:int || long || etc”之类的东西,我会好的。 (并且无需解释为什么他们不支持,原因相当明显)
非常感谢任何帮助!
编辑:我意识到我可以做一堆重载,每个类型一个我想支持...但是我问这个问题因为我想要避免这个:)
答案 0 :(得分:3)
我不同意这不可能做到;只是我建议做的设计有点奇怪(并且参与其中)。
这是个主意。
使用一种方法定义接口IBytesProvider<T>
:
public interface IBytesProvider<T>
{
byte[] GetBytes(T value);
}
然后在具有静态BytesProvider<T>
属性的Default
类中实现此功能。
如果这听起来很熟悉,那是因为它正是EqualityComparer<T>
和Comparer<T>
类的工作原理(在大量LINQ扩展方法中大量使用)。
以下是我建议您进行设置的方法。
public class BytesProvider<T> : IBytesProvider<T>
{
public static BytesProvider<T> Default
{
get { return DefaultBytesProviders.GetDefaultProvider<T>(); }
}
Func<T, byte[]> _conversion;
internal BytesProvider(Func<T, byte[]> conversion)
{
_conversion = conversion;
}
public byte[] GetBytes(T value)
{
return _conversion(value);
}
}
static class DefaultBytesProviders
{
static Dictionary<Type, object> _providers;
static DefaultBytesProviders()
{
// Here are a couple for illustration. Yes, I am suggesting that
// in reality you would add a BytesProvider<T> for each T
// supported by the BitConverter class.
_providers = new Dictionary<Type, object>
{
{ typeof(int), new BytesProvider<int>(BitConverter.GetBytes) },
{ typeof(long), new BytesProvider<long>(BitConverter.GetBytes) }
};
}
public static BytesProvider<T> GetDefaultProvider<T>()
{
return (BytesProvider<T>)_providers[typeof(T)];
}
}
现在,终于,一旦你完成了这一切,你所做的就是致电:
byte[] bytes = BytesProvider<T>.Default.GetBytes(toAdd);
不需要重载。
答案 1 :(得分:2)
您可以通过首先将方法分为两部分来完成此操作,一部分将值转换为字节数组,另一部分插入它们。然后只使用重载:
public static void AddToByteArray(byte[] destination, int offset, long value)
{ InsertBytes(destination, offset, BitConverter.GetBytes(value)); }
public static void AddToByteArray(byte[] destination, int offset, int value)
{ InsertBytes(destination, offset, BitConverter.GetBytes(value)); }
public static void AddToByteArray(byte[] destination, int offset, short value)
{ InsertBytes(destination, offset, BitConverter.GetBytes(value)); }
private static void InsertBytes(byte[] destination, int offset, byte[] bytes)
{
Buffer.BlockCopy(bytes, 0, destination, offset, bytes.Length);
}
答案 2 :(得分:1)
无论如何这都行不通,因为要使用的BitConverter.GetBytes()
的重载在编译时而不是在运行时解决,所以传递为T
的泛型参数不会用于帮助确定GetBytes()
重载。由于没有接受object
的重载,因此即使可以约束T
某些特定类型的类型,此方法也无法正常工作。所以你在这里倍加紧张。
这里唯一真正的选择是为您要接受的每种数字类型重载AddToByteArray
方法。我知道你不想这样做,但你无能为力。 (您可以接受object
的参数,并使用反射根据参数类型调用GetBytes()
的特定重载,但由于反射和装箱开销,这将是狗慢...)
答案 3 :(得分:1)
作为Dan Tao解决方案的简化,结合SLaks的建议,这是一个完整的通用BitConverter:
public class BitConverter<T>
{
public static readonly Func<T, byte[]> GetBytes = x => new byte[] { };
static BitConverter()
{
BitConverter<byte>.GetBytes = x => new byte[] { x };
BitConverter<bool>.GetBytes = x => BitConverter.GetBytes(x);
BitConverter<char>.GetBytes = x => BitConverter.GetBytes(x);
BitConverter<double>.GetBytes = x => BitConverter.GetBytes(x);
BitConverter<Int16>.GetBytes = x => BitConverter.GetBytes(x);
BitConverter<Int32>.GetBytes = x => BitConverter.GetBytes(x);
BitConverter<Int64>.GetBytes = x => BitConverter.GetBytes(x);
BitConverter<Single>.GetBytes = x => BitConverter.GetBytes(x);
BitConverter<UInt16>.GetBytes = x => BitConverter.GetBytes(x);
BitConverter<UInt32>.GetBytes = x => BitConverter.GetBytes(x);
BitConverter<UInt64>.GetBytes = x => BitConverter.GetBytes(x);
}
}
您的示例将如下所示:
private void AddToByteArray<T>(byte[] destination, int offset, T toAdd) where T : struct
{
Buffer.BlockCopy(BitConverter<T>.GetBytes(toAdd), 0, destination, offset, sizeof(toAdd));
}
您只需要为静态构造函数的每个预期类型提供GetBytes()方法。顺便说一下,这不仅限于结构类型,例如:
BitConverter<MemoryStream>.GetBytes = x => x.ToArray();
BitConverter<string>.GetBytes = x => Encoding.Default.GetBytes(x);
如果泛型类型没有GetBytes()方法,它将返回一个空数组(但您可能希望更改代码以引发异常!)。
答案 4 :(得分:0)
这是C#中的一个真正的问题,没有所有数字类型实现的通用接口,您可以限制为struct
和new()
,但仍然允许使用无参数构造函数的结构。如果你真的想限制它,你可能不得不为所有数字类型使用定义的重载。
答案 5 :(得分:0)
如果您只关心在小端系统(如Windows)上运行,您可以在IConvertible上添加约束(我相信所有数字类型都支持),使用它将值转换为64位整数,获取该字节,然后丢弃您不需要的字节。像这样:
private byte[] NumberToBytes<T>(T value)
where T : new(), struct, IConvertible
{
var longValue = value.ToUInt64();
var bytes = BitConverter.GetBytes(longValue);
Array.Resize(ref bytes, sizeof(T));
return bytes;
}
当然,这假设您只使用整数 - 它不适用于float,double或decimal。如上所述,它只适用于小端系统;对于big-endian,你需要保留数组中的最后sizeof(T)
个元素,而不是第一个。