首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >在常规数组上测试foreach box/unbox?

在常规数组上测试foreach box/unbox?
EN

Stack Overflow用户
提问于 2012-12-26 09:05:17
回答 4查看 998关注 0票数 3

我读过this excellent question关于如何在foreach (框或非)循环下处理常规int[]的文章。

Array确实实现了非泛型IEnumerable,因此它必须在内部使用object(而不是int)。

但是在运行时它实际上是作为turns out处理的。

我如何通过简单的C#代码来测试/证明(没有装箱)?(而不是通过读取IL)。

EN

回答 4

Stack Overflow用户

回答已采纳

发布于 2012-12-26 09:42:02

您的问题错误地假设数组没有实现泛型IEnumerable<T> (正如您链接到的问题一样)。确实如此。您可以使用反射看到这一点:

代码语言:javascript
复制
var array = new int[0];
var enumerator = array.GetEnumerator();
var enumeratorType = enumerator.GetType();
var propertyInfo = enumeratorType.GetProperty("Current");
var propertyType = propertyInfo.PropertyType;
Console.WriteLine(propertyType.Name); //prints "Object";
var otherEnumerator = ((IEnumerable<int>)array).GetEnumerator();
enumeratorType = otherEnumerator.GetType();
propertyInfo = enumeratorType.GetProperty("Current");
propertyType = propertyInfo.PropertyType;
Console.WriteLine(propertyType.Name); //prints "Int32";

但是,如果您在静态类型数组引用上编写了foreach循环,则C#编译器将其转换为for循环。我不认为在不看IL的情况下是没有办法检查的。

来自http://msdn.microsoft.com/en-us/library/system.array.aspx

重要

从.NET Framework2.0开始,数组类实现了System.Collections.Generic.IList<T>System.Collections.Generic.ICollection<T>System.Collections.Generic.IEnumerable<T>泛型接口。这些实现在运行时提供给数组,因此在文档构建工具中是不可见的。因此,泛型接口不会出现在Array类的声明语法中,而且只有通过将数组转换为泛型接口类型(显式接口实现),才能访问接口成员的引用主题。当您将数组强制转换到这些接口之一时,需要注意的关键是,添加、插入或删除元素的成员抛出NotSupportedException。

票数 4
EN

Stack Overflow用户

发布于 2012-12-26 10:17:29

我喜欢@phoog的答案,所以只是为了好玩:)

Helper类

代码语言:javascript
复制
public static class ILUtils
{
    private static Dictionary<short, OpCode> s_opcodes = new Dictionary<short, OpCode>();

    static ILUtils()
    {
        FieldInfo[] opCodeFields = typeof(OpCodes).GetFields(BindingFlags.Public | BindingFlags.Static);
        foreach (FieldInfo opCodeField in opCodeFields)
        {
            if (opCodeField.FieldType != typeof(OpCode))
                continue;

            OpCode opcode = (OpCode)opCodeField.GetValue(null);
            s_opcodes.Add(opcode.Value, opcode);
        }
    }

    public static bool ContainsOpcodes(MethodInfo methodInfo, IEnumerable<OpCode> targetOpCodes)
    {
        MethodBody methodBody = methodInfo.GetMethodBody();

        using (BinaryReader ilReader = new BinaryReader(new MemoryStream(methodBody.GetILAsByteArray())))
        {
            while (ilReader.BaseStream.Position < ilReader.BaseStream.Length)
            {
                short opCodeValue = ilReader.ReadByte();
                if (opCodeValue == 0xfe)
                    opCodeValue = (short)(opCodeValue << 8 | ilReader.ReadByte());

                OpCode opCode = s_opcodes[opCodeValue];
                if (targetOpCodes.Contains(opCode))
                    return true;

                int argumentSize = 4;
                if (opCode.OperandType == OperandType.InlineNone)
                    argumentSize = 0;
                else if (opCode.OperandType == OperandType.ShortInlineBrTarget || opCode.OperandType == OperandType.ShortInlineI || opCode.OperandType == OperandType.ShortInlineVar)
                    argumentSize = 1;
                else if (opCode.OperandType == OperandType.InlineVar)
                    argumentSize = 2;
                else if (opCode.OperandType == OperandType.InlineI8 || opCode.OperandType == OperandType.InlineR)
                    argumentSize = 8;
                else if (opCode.OperandType == OperandType.InlineSwitch)
                {
                    int num = ilReader.ReadInt32();
                    argumentSize = (int)(4 * num + 4);
                }

                ilReader.BaseStream.Position += argumentSize;
            }
        }

        return false;
    }
}

示例用法

代码语言:javascript
复制
private static void BoxingForEach()
{
    IEnumerable foo = (IEnumerable)new int[10];
    foreach (int i in foo) ;
}

private static void NoBoxingForEach()
{
    int[] foo = new int[10];
    foreach (int i in foo) ;
}

static void Main(string[] args)
{
    MethodInfo boxingForEach = typeof(Program).GetMethod("BoxingForEach", BindingFlags.Static | BindingFlags.NonPublic);
    MethodInfo noBoxingForEach = typeof(Program).GetMethod("NoBoxingForEach", BindingFlags.Static | BindingFlags.NonPublic);

    Console.WriteLine("BoxingForEach is using boxing: {0}", 
        ILUtils.ContainsOpcodes(boxingForEach, new[] { OpCodes.Box, OpCodes.Unbox, OpCodes.Unbox_Any }));

    Console.WriteLine("NoBoxingForEach is using boxing: {0}", 
        ILUtils.ContainsOpcodes(noBoxingForEach, new[] { OpCodes.Box, OpCodes.Unbox, OpCodes.Unbox_Any }));
}

结果

BoxingForEach正在使用装箱: True NoBoxingForEach正在使用装箱: False

票数 5
EN

Stack Overflow用户

发布于 2012-12-26 09:59:13

如果不看发射的IL,就很难验证拳击是否在幕后进行。

但是试试这个:

代码语言:javascript
复制
static void Main()
{
  int[] arr1 = { 7, 9, 13, };
  Array arr2 = arr1;
  IEnumerable arr3 = arr1;  // non-generic IEnumerable

  foreach (var x in arr1)  // hold mouse over var keyword to see compile-time type
  {
    Overloaded(x);  // go to definition to see which overload is used
  }
  foreach (var x in arr2)  // hold mouse over var keyword to see compile-time type
  {
    Overloaded(x);  // go to definition to see which overload is used
  }
  foreach (var x in arr3)  // hold mouse over var keyword to see compile-time type
  {
    Overloaded(x);  // go to definition to see which overload is used
  }
}

static void Overloaded(int x)
{
  Console.WriteLine("int!");
}
static void Overloaded(object x)
{
  Console.WriteLine("object!");
}

很容易看出,拳击确实是与arr2arr3一起进行的。从技术上讲,我们不能确定arr1是否会发生装箱(必须检查IL),但我们可以看到隐式类型(var)变量变成了int变量,这是一种线索。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/14038264

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档