C# 获取数组的长度

C# 获取数组的长度,c#,.net,arrays,reflection,C#,.net,Arrays,Reflection,所以我上了这门课: public class MyClass{ public int[] myArray = new int[9]; } 我可以在不需要初始化MyClass实例的情况下获取数组的长度吗 无需初始化MyClass的实例 否。=new int[9]被移动到构造函数,该构造函数仅在实例化类时运行。如果不创建实例或使成员为静态,则不可能 FieldInfo(通过反射)将为您提供一个事实,即它是一个int32[],但这是它所能得到的。如果长度是常量(从您的

所以我上了这门课:

    public class MyClass{
        public int[] myArray = new int[9];
    }
我可以在不需要初始化MyClass实例的情况下获取数组的长度吗

无需初始化MyClass的实例


否。
=new int[9]
被移动到构造函数,该构造函数仅在实例化类时运行。

如果不创建实例或使成员为静态,则不可能

FieldInfo(通过反射)将为您提供一个事实,即它是一个int32[],但这是它所能得到的。

如果长度是常量(从您的示例中看起来,因为它似乎没有使用构造函数中作为参数提供的任何值),您可以将长度设置为常量字段,并随时访问它,因为常量是隐式静态的

或者,正如有人在评论中所说的,在类中标记一个属性,该属性在不引入新成员的情况下提供这些信息,这也是一种有效的惯用方法

另一方面,如果长度不是常数,但您的场景仍然足够简单,那么您可以使用类似Mono Cecil的库,对程序集进行反思,找到此特定类型及其构造函数,检查构造函数的IL,并推断如果它运行,它将在堆栈上放置的值。这是完全可能的,但是很痛苦。

你可以这样做:

public class MyClass {
    public static readonly int arrayLength = 9;
    public int[] myArray = new int[MyClass.arrayLength];
}
然后:


不太可能,但可以不使用“MyClass”进行实例化。 然后获取数组的值

创建接口IMyClass并使用Activator实例化该类,如下所示:

var _type = typeof(IMyClass);

var _types = AppDomain.CurrentDomain.GetAssemblies().SelectMany(_s => _s.GetTypes()).Where(_p => _type.IsAssignableFrom(_p) && !_p.IsInterface);

foreach (var _instance in _types)
{
    var _instance = (IMyClass)Activator.CreateInstance(_instance));
    _instance.myArray.Length; 
}

您可以解析构造函数的IL代码

var constructor = typeof(MyClass).GetConstructor(Type.EmptyTypes);
var body = constructor.GetMethodBody();
byte[] il = body.GetILAsByteArray();
这是你的IL代码。。。然后您需要一个IL解析器,然后您应该获得如下内容:

IL_0000: ldarg.0
IL_0001: ldc.i4.5
IL_0002: newarr [mscorlib]System.Int32
IL_0007: stfld int32[] ExpressionProblem.MyClass::MyArray
IL_000c: ldarg.0
然后开始从
int32[]ExpressionProblem.MyClass::MyArray

嘿。。。我没有告诉你这是一个实际的解决方案。这是一个可能的解决办法

好的。。。我准备了一个工作示例:

/// <summary>
/// Supports only direct array sizing with values 0...int.MaxValue .
/// Doesn't support: values greater than int.MaxValue, static values,
/// function calling, ...
/// </summary>
/// <param name="type"></param>
/// <param name="arrayName"></param>
/// <param name="instance"></param>
/// <returns></returns>
public static int GetSize(Type type, string arrayName, bool instance)
{
    BindingFlags bindingFlags = (instance ? BindingFlags.Instance : BindingFlags.Static) | BindingFlags.Public | BindingFlags.NonPublic;

    // The array
    FieldInfo arrayField = type.GetField(arrayName, bindingFlags);

    // We don't know which constructor does the initialization, so we 
    // check each one. We start with the first one, and then we will 
    // follow the chain of constructors
    ConstructorInfo constructor = type.GetConstructors(bindingFlags).FirstOrDefault();

    while (constructor != null)
    {
        ConstructorInfo nextConstructor = null;

        var instructions = Mono.Reflection.Disassembler.GetInstructions(constructor);

        int i;

        for (i = 0; i < instructions.Count; i++)
        {
            if (instructions[i].OpCode == OpCodes.Call)
            {
                nextConstructor = instructions[i].Operand as ConstructorInfo;

                // If there is a call to another constructor, then 
                // this isn't the method we are looking for :-)
                if (nextConstructor != null)
                {
                    if (constructor.DeclaringType != nextConstructor.DeclaringType)
                    {
                        // Going to base class constructor without 
                        // initializing the field we are interested 
                        // in. We can stop looking.
                        nextConstructor = null;
                    }

                    i = instructions.Count;
                    break;
                }
            }

            // We look for a Stfld operation on the array
            if (instructions[i].OpCode == OpCodes.Stfld && (instructions[i].Operand as FieldInfo) == arrayField)
            {
                break;
            }
        }

        // Access to the array wasn't found. Let's look at the next 
        // constructor
        if (i == instructions.Count)
        {
            constructor = nextConstructor;
            continue;
        }

        // There are too few instructions before this array access
        if (i - 2 < 0)
        {
            throw new NotSupportedException();
        }

        OpCode newArr = instructions[i - 1].OpCode;

        // Is the previous instruction a NewArr?
        if (newArr != OpCodes.Newarr)
        {
            throw new NotSupportedException();
        }

        var sizeInstruction = instructions[i - 2];

        // Calc the size. There are various opcodes for this.
        int size;

        if (sizeInstruction.OpCode == OpCodes.Ldc_I4)
        {
            size = (int)sizeInstruction.Operand;
        }
        else if (sizeInstruction.OpCode == OpCodes.Ldc_I4_0)
        {
            size = 0;
        }
        else if (sizeInstruction.OpCode == OpCodes.Ldc_I4_1)
        {
            size = 1;
        }
        else if (sizeInstruction.OpCode == OpCodes.Ldc_I4_2)
        {
            size = 2;
        }
        else if (sizeInstruction.OpCode == OpCodes.Ldc_I4_3)
        {
            size = 3;
        }
        else if (sizeInstruction.OpCode == OpCodes.Ldc_I4_4)
        {
            size = 4;
        }
        else if (sizeInstruction.OpCode == OpCodes.Ldc_I4_5)
        {
            size = 5;
        }
        else if (sizeInstruction.OpCode == OpCodes.Ldc_I4_6)
        {
            size = 6;
        }
        else if (sizeInstruction.OpCode == OpCodes.Ldc_I4_7)
        {
            size = 7;
        }
        else if (sizeInstruction.OpCode == OpCodes.Ldc_I4_8)
        {
            size = 8;
        }
        else if (sizeInstruction.OpCode == OpCodes.Ldc_I4_M1)
        {
            size = -1;
        }
        else if (sizeInstruction.OpCode == OpCodes.Ldc_I4_S)
        {
            size = (sbyte)sizeInstruction.Operand;
        }
        else
        {
            // The size of the array was calculated in some other 
            // way. Not supported :-(
            throw new NotSupportedException();
        }

        return size;
    }

    throw new NotSupportedException();
}

请注意,我不认为这是个好主意,除非你真的需要它:)

事实上,有一种方法可以做到这一点。请看这里的汇编代码

.method public hidebysig specialname rtspecialname 
    instance void  .ctor() cil managed
// SIG: 20 00 01
{
  // Method begins at RVA 0x2050
  // Code size       21 (0x15)
  .maxstack  8
  IL_0000:  /* 02   |                  */ ldarg.0
  IL_0001:  /* 1F   | 09               */ ldc.i4.s   9
  IL_0003:  /* 8D   | (01)000013       */ newarr     [mscorlib]System.Int32
  IL_0008:  /* 7D   | (04)000001       */ stfld      int32[] ConsoleApplication2.MyClass::myArray
  IL_000d:  /* 02   |                  */ ldarg.0
  IL_000e:  /* 28   | (0A)000011       */ call       instance void [mscorlib]System.Object::.ctor()
  IL_0013:  /* 00   |                  */ nop
  IL_0014:  /* 2A   |                  */ ret
}//方法MyClass的结尾::.ctor

我们对这两种说明感兴趣

ldc.i4.s   9
newarr     [mscorlib]System.Int32
然后使用反射,我们可以得到构造函数的
MethodBody
,从中我们可以得到IL字节。问题是反射实际上不允许您从IL的角度查看现有IL,而只允许您发射它。但是,我们知道我们需要的指令的字节是什么,所以

ConstructorInfo constructor=typeof(MyClass).GetConstructors().First()

此代码的问题在于,对于不同的数字,它的ldc.i4将不同,因此它仅在长度介于9和256之间时才起作用

当然,我知道这只适用于这个确切的代码,但是经过一些修改和检查,有一种方法可以使它适用于几乎任何知道数组名称的类


另外,我知道没有理由这样做,但这里的每个人都说这是不可能的,这是不对的。

不,因为这不是该类型的静态属性,而是实例属性-实例是必需的!只是出于兴趣,你为什么需要知道这些?如果你需要一些编译时信息,属性是一种方法。但是有一种方法可以做到这一点。在编写构造函数中显式编写的任何其他代码之前,像这样的每次初始化都会被放入构造函数代码中。您可以通过使用反射并查看构造函数指令集来获得数组的长度。@Spo1ler这是一种可能性,但对于一般问题,它不是解决方案(也就是说:您肯定不希望这样)。OP很可能在这里解决了一个XY问题,其中Y是“因此我需要使用反射来获得数组长度,而不需要实例化类”。OP忘了提到问题X。因此,对于一个实例,您可以这样得到它:
((数组)typeof(MyClass).GetField(“myArray”).GetValue(new MyClass()).Length
是的,或者干脆
new MyClass().myArray.Length
。。。不确定OP试图做什么。@Selman22:
((数组)typeof(MyClass2).GetField(“myArray”,BindingFlags.Instance | BindingFlags.NonPublic).GetValue(new MyClass2()).Length尝试使用
新建MyClass().myArray.Length
;)来完成此操作你不理解,你仍然在创建一个类的实例,不管你是使用
Activator
还是仅仅
new
来创建它?是的,我理解,因此我说,“…但是你可以不使用“MyClass”来实例化它…”。在预先编辑好的问题中,有一些类似于反思的东西。这就是为什么我在回答中提到了这一点。嗯,你的回答肯定比我的更详细,很好。
.method public hidebysig specialname rtspecialname 
    instance void  .ctor() cil managed
// SIG: 20 00 01
{
  // Method begins at RVA 0x2050
  // Code size       21 (0x15)
  .maxstack  8
  IL_0000:  /* 02   |                  */ ldarg.0
  IL_0001:  /* 1F   | 09               */ ldc.i4.s   9
  IL_0003:  /* 8D   | (01)000013       */ newarr     [mscorlib]System.Int32
  IL_0008:  /* 7D   | (04)000001       */ stfld      int32[] ConsoleApplication2.MyClass::myArray
  IL_000d:  /* 02   |                  */ ldarg.0
  IL_000e:  /* 28   | (0A)000011       */ call       instance void [mscorlib]System.Object::.ctor()
  IL_0013:  /* 00   |                  */ nop
  IL_0014:  /* 2A   |                  */ ret
ldc.i4.s   9
newarr     [mscorlib]System.Int32
byte[] constructorBytes = constructor.GetMethodBody().GetILAsByteArray();

int length = constructorBytes[Array.FindIndex(constructorBytes, b => b == 0x1F) + 1];