如果你有一個Type of Type Array而不是一個特定的數組類型,比如int [],那麼如何生成一個表達式,該表達式可以快速獲取長度,而無需執行整個屬性獲取malarkay。
例如在下面
ParameterExpression para3 = Expression.Parameter(typeof(int[]), "p3");
ParameterExpression para4 = Expression.Parameter(typeof(Array), "p4");
Type pt1 = para3.Type.GetElementType();
Type pt2 = para4.Type.GetElementType();
MethodInfo mArrayLength = Strong.Instance<Array>.Property<int>(a => a.Length).GetGetMethod();
Expression asdf5 = Expression.ArrayLength(para3);
Expression asdf6 = Expression.ArrayLength(para4);
Expression asdf7 = Expression.Call(para4, mArrayLength);
mArrayLength只是Array類型的Length屬性的get方法。
這裡的表達式為asdf5,因為para5的類型為int [],但是asdf6 沒有,因為para6的類型只是Array類型。 asdf7確實有效。
我想要的是有效地使用ldlen指令,它只需要一個Object,而不是調用一個方法。這僅僅是表達式樹庫的限制嗎?
您可以使用反射編輯該字段,甚至可以編譯表達式!但是嘗試運行委託將導致操作可能破壞運行時異常的穩定性。
Array parr = new int[5];
Expression pArraylength = Expression.ArrayLength(para3);
pOperandFieldInfo.SetValue(pArraylength, para4);
Expression<Func<Array, int>> pexchanger = (Expression<Func<Array, int>>)Expression.Lambda(pArraylength, para4);
Func<Array, int> pFunc = pexchanger.Compile();
int pint = pFunc(parr);
.NET中有兩種類型的數組,單向,從零開始(第一個索引是0)數組(例如int[]
),它們由IL語言(以及Expression
類)直接支持(如一個側面說明它們被稱為SZ數組),其他數組(多維數據庫,例如int[5,3]
和第一個索引不同的數組存在,以便與支持它們的某些語言兼容,例如舊的VB) ,它在IL語言中沒有直接支持,但是使用對Array
類的調用的編譯器支持。它們基於兩個不同的類,它們都是Array
子類。正如我所寫,低級IL指令僅適用於單維,零基指令。 Array
對象可以是兩者,因此Expression.ArrayLength
不支持。
你可以在SharpLab中輕鬆看到這個:
public static int A1<T>(T[] array) {
return array.Length;
}
public static int A2(Array array) {
return array.Length;
}
.method public hidebysig static
int32 A1<T> (
!!T[] 'array'
) cil managed
{
// Method begins at RVA 0x2050
// Code size 4 (0x4)
.maxstack 8
IL_0000: ldarg.0
IL_0001: ldlen
IL_0002: conv.i4
IL_0003: ret
} // end of method C::A1
.method public hidebysig static
int32 A2 (
class [mscorlib]System.Array 'array'
) cil managed
{
// Method begins at RVA 0x2055
// Code size 7 (0x7)
.maxstack 8
IL_0000: ldarg.0
IL_0001: callvirt instance int32 [mscorlib]System.Array::get_Length()
IL_0006: ret
} // end of method C::A2
只是出於好奇,使用了一個沒有記錄的功能(我們在這裡玩火!)......
ParameterExpression par = Expression.Parameter(typeof(Array), "array");
var conv = Expression.New(typeof(Conv));
var init = Expression.MemberInit(conv, Expression.Bind(typeof(Conv).GetField(nameof(Conv.Array)), par));
var array = Expression.Field(init, nameof(Conv.Array2));
var length = Expression.ArrayLength(array);
var lambda = Expression.Lambda<Func<Array, int>>(length, par);
var compiled = lambda.Compile();
Conv
是:
[StructLayout(LayoutKind.Explicit)]
public struct Conv
{
[FieldOffset(0)]
public Array Array;
[FieldOffset(0)]
public byte[] Array2;
}
使用它像:
int length = compiled(new int[100]);
這裡的“技巧”是通過FieldOffest
我們可以FieldOffest
具有相同內存佈局的不兼容類型。所有sz數組共享相同的頭格式(包含Length
位置),因此我們將我們的Array
“轉換”為byte[]
數組(注意我們可能已經將它轉換為任何其他類型,但是有一個明顯的優勢在一個byte
:它是最小的可用類型,因此我們確信我們不能以任何方式“退出”數組。
表達式樹類似於:
static int GetLength(Array array)
{
return new Conv { Array = array }.Array2.Length;
}