BitConverter类中的IsLittleEndian有哪些用途?

9

当我发现BitConverter中的IsLittleEndian字段时,我感到非常高兴。我认为它应该存在,并且我应该能够指定任何字节序。然而,我的快乐并没有持续多久。花了一些时间才发现无法设置该字段。 该字段是readonly的,并且仅在静态构造函数中设置为true

static BitConverter()
{
    IsLittleEndian = true;
}

有趣的是,该字段实际上在代码中被使用。例如,ToInt32方法的实现看起来像这样:

if (IsLittleEndian)
{
     return (((numRef[0] | (numRef[1] << 8)) | (numRef[2] << 0x10)) | (numRef[3] << 0x18));
}
return ((((numRef[0] << 0x18) | (numRef[1] << 0x10)) | (numRef[2] << 8)) | numRef[3]);

看起来 ToInt32 完全能够处理小端和大端的情况。
我的问题是:为什么有一个非常有用的代码片段已经在 FCL 中实现并存在,但是没有办法使用它(除非你开始使用反射)?这只是因为一些开发人员没有达到最后期限而留下了未完成的工作吗?即使是这样,为什么代码不可用,但字段却可以?
我希望这背后有一个很好的理由。
我想让自己清楚。我不需要处理大端值的解决方案。我已经有了一个解决方案。实际上,我的问题中已经展示了解决方案。
7个回答

6
答案在查看BitConverter类的参考来源中。相关摘录如下:
        // This field indicates the "endianess" of the architecture.
        // The value is set to true if the architecture is
        // little endian; false if it is big endian.
#if BIGENDIAN
        public static readonly bool IsLittleEndian /* = false */;
#else
        public static readonly bool IsLittleEndian = true;
#endif

该标志是由预处理器指令硬编码的,因为特定版本框架所编译的架构的字节序不会改变。


6

不幸的是,IsLittleEndian字段只是提供信息。但是Jon Skeets MiscUtil库有一个很好的EndianBitConverter,支持小端和大端。还有端序感知的BinaryWriter/-Reader类。

这是链接:http://www.yoda.arachsys.com/csharp/miscutil/

编辑:抱歉,我没有更好的解释。我认为这应该已经包含在框架中了,我猜代码目前也在那里,所以很容易将转换器移植到另一种架构中。

但是公开此功能比仅仅将字段公开要复杂得多。转换器是静态的,因此更改标志实际上会更改全局状态,在多线程场景下这将是灾难性的。可能的方法是提供两个BitConverter对象,您可以实例化并在本地使用(这就是MiscUtil所做的)。这需要额外的类和/或接口,因此可能是截止日期问题,暂时被放弃了。让我们希望它稍后会被添加。


这不完全是我的问题的答案。您只是重新陈述了我在问题中已经陈述的事实,即无法设置“IsLittleEndian”。同时,您提出了一个解决方案,但我已经有一个解决方案,而且我也没有要求解决方案。请仔细阅读问题。 - Alex Aza
回答后我看到了你的编辑,不太确定你是否需要解决方案(但我认为提及 MiscUtil 不会有坏处)。我已经在答案中添加了一些我的想法。 - ollb

5
首先,让我们明确一点,类BitConverter专门设计用于本地处理器的位转换。这就是为什么IsLittleEndian是只读的原因。因此,如果本地处理器是小端字节序,则不支持转换到或从大端字节序,反之亦然。
虽然我不知道为什么省略了对通用字节序的支持,但在我看来最合理的原因是性能。一个广泛用于框架中的类(用于转换到和从本地处理器的字节序)应尽可能高效。通过限制类的普适性,可以通过限制必须处理的情况来提高其性能。仅支持小端字节序很可能更快。
好的,现在我们来到问题的核心。如果该类的整体设计只打算支持一种字节序,为什么作者会包含处理小端字节序和大端字节序的代码呢?
同样,我们只能推测。但答案可能在两个观察结果中得出:
  • 引用IsLittleEndian的反汇编代码在性能方面是不重要的
  • 编写可移植的代码,如果不影响性能,是良好的软件工程
你从ToInt32方法复制的代码之所以不重要,是因为它仅用于非对齐内存。99%的代码路径是直接使用不安全的“memcpy”位拷贝。
即使转换非对齐内存,处理它的代码效率比原始方法低一个数量级。因此,额外的条件并不会影响性能。
最终结果是:
  • BitConverter类在其有限的目的上是尽可能高效的
  • BitConverter的源代码仍然可以移植到大端字节序的处理器体系结构中

1
根据MSDN文档IsLittleEndian,它仅仅是为了告诉你(你的程序或BitConverter类)体系结构是小端还是大端。我认为除此之外没有其他预期用途。

尽管文档上说IsLittleEndian的值可能是true或者false,但是Reflector显示它总是true,而且没有办法将其改为false。我需要将其设为false - Alex Aza
它始终为假,因为您的计算机是32位的。 - David Wick
3
@David Wick - 说真的吗?你刚才是在胡编乱造这个解释吗? :) - Alex Aza
1
@David Wick:字节序与位数无关,而是取决于处理器类型。x86和x64处理器是小端处理器。 - dtb
是的,我现在明白了。我太快地浏览了维基百科。 - David Wick

0

它是基于体系结构类型内部设置的。

来自文档

“不同的计算机体系结构使用不同的字节顺序存储数据。“大端”表示最高位字节在单词的左端。“小端”表示最高位字节在单词的右侧。”

编辑:

这是C#团队做出的设计决策。该函数能够在两种类型的系统上进行转换,因此您作为开发人员需要以其他方式进行转换。

“…BitConverter的所有方法都接受或返回SYSTEM字节顺序的字节数组…”

int i = BitConverter.ToInt32(byte[] inputdata);
(manipulate i)
return BitConverter.GetBytes(i);

"

如果数据从未离开您的应用程序,这种方式实际上可以正常工作。

有关更多信息,请阅读this article

"

@David Wick - 你真的认为我没有阅读文档吗?另外,我喜欢你的评论“你永远不会手动使用这个字段。”并不是我会或不会,我确实需要使用它。我接收到由硬件设备生成的字节流,我需要将数据转换为int列表。我不能使用BitConverter,因为日期是大端序。 - Alex Aza
首先使用字节交换将数据转换为小端格式。 - David Wick
@David Wick - 我知道如何处理大端。我的问题实际上展示了处理大端的代码。我的问题不是关于如何做到这一点。我的问题是为什么 BitConverter 有处理大端的代码,但我不能使用这些代码。 - Alex Aza
它如何知道字节流是大端序还是小端序?这只是一串字节流而已。他们假设数据来自同一台机器。作为开发人员,你需要进行转换。 - David Wick
看起来我在将x64 / x86分别称为大/小端时是错误的。 我基于文档示例做出了这个假设。 无论如何,你明白我的意思。 - David Wick
显示剩余2条评论

0

我相信他们将其设置为true,没有可能的false,因为所有版本的Windows都是小端字节序

现在,他们在一个从未将IsLittleEndian设置为除true以外的任何值的类中执行if (IsLittleEndian)的问题很可能是一种预防措施。这样,如果需要为大端编译.NET BCL,只需在那个赋值周围加上一个简单的#if/#else即可,而不必编写新代码。

我敢打赌,对于某些操作系统和架构,Mono将其设置为false

编辑:我是正确的。Mono执行以下操作。从技术上讲,除了由于版权问题而不得不在Mono中以不同方式编写所有内容之外,其他代码都不需要添加。

public static readonly bool IsLittleEndian = AmILittleEndian ();

static unsafe bool AmILittleEndian ()
{
  // binary representations of 1.0:
  // big endian: 3f f0 00 00 00 00 00 00
  // little endian: 00 00 00 00 00 00 f0 3f
  // arm fpa little endian: 00 00 f0 3f 00 00 00 00
  double d = 1.0;
  byte *b = (byte*)&d;
  return (b [0] == 0);
}

是的,我知道这是硬编码的,但我的问题是为什么实际上有一段代码可以处理truefalse - Alex Aza
我也给BCL团队发送了电子邮件。 - Joel B Fant
你从BCL团队那里收到回复了吗? - Alex Aza
@Alex Aza:还没有。我实际上先想给 Eric Lippert 发电子邮件,他可能有答案,但专注于不同的领域。所以我先给适当的团队发了电子邮件。虽然我倾向于等待超过1周。 - Joel B Fant


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接