我正在嘗試實作一個函式,該函式回傳計算向量標量積的函式。它應該通過泛型實作,但似乎只能通過在運行時生成代碼來實作。通過構建運算式樹閱讀有關代碼生成的幾個檔案,這是我到目前為止所寫的:
public static Func<T[], T[], T> GetVectorMultiplyFunction<T>()
where T : struct
{
ParameterExpression first = Expression.Parameter(typeof(T[]), "first" );
ParameterExpression second = Expression.Parameter(typeof(T[]), "second");
ParameterExpression result = Expression.Parameter(typeof(T) , "result");
ParameterExpression index = Expression.Parameter(typeof(int), "index" );
LabelTarget label = Expression.Label(typeof(T));
BlockExpression block = Expression.Block(
new[] { result, index },
Expression.Assign( result, Expression.Constant(0) ),
Expression.Assign( index , Expression.Constant(0) ),
Expression.Loop(
Expression.IfThenElse(
Expression.LessThan( index, Expression.ArrayLength( first ) ),
Expression.Block(
Expression.AddAssign( result, Expression.Multiply( Expression.ArrayIndex( first, index ), Expression.ArrayIndex( second, index ) ) ),
Expression.Increment( index )
),
Expression.Break( label, result )
),
label
)
);
return Expression
.Lambda<Func<T[], T[], T>>( block, first, second )
.Compile();
}
這構建沒有問題,但需要永遠運行測驗。我很難繞開這個話題。所以我不知道到底出了什么問題。
這是使用此方法的一個測驗:
[Test]
public void GetVectorMultiplyFunctionReturnsFunctionForLong()
{
var first = new long[] { 1L, 2L, 3L };
var second = new long[] { 2L, 2L, 2L };
var expected = 1L * 2L 2L * 2L 3L * 2L;
var func = CodeGeneration.GetVectorMultiplyFunction<long>();
var actual = func(first, second);
Assert.AreEqual(expected, actual);
}
uj5u.com熱心網友回復:
在 Linqpad 中進行一些除錯后,問題不在于您的動態方法“慢”(不是),問題在于該方法包含一個永不退出的無限回圈。
據我所知,如果您的GetVectorMultiplyFunction方法是直接用 C# 撰寫的,那么它的目的是做這樣的事情:
static T[] VectorMultiply<T>( T[] first, T[] second )
where T : struct
{
T result = default(T);
Int32 index = 0;
while( true )
{
if( index < first.Length )
{
result = first[index] * second[index];
index ;
}
else
{
return result;
}
}
}
...但是您的代碼中有一些主要錯誤:
-
ParameterExpression result = Expression.Parameter(typeof(T) , "result"); ParameterExpression index = Expression.Parameter(typeof(int), "index" );- 這兩行應該使用
Expression.Variable, 不是Expression.Parameterasresult也不index是方法引數,而是方法區域變數。
- 這兩行應該使用
-
Expression.Assign( result, Expression.Constant(0) )- 這不起作用,因為
result型別為T,但Expression.Constant(0)型別為Int32( 因為0文字是Int32文字。 - 將其更改為 use
default(T),如下所示:Expression.Assign( result, Expression.Constant( default(T) ) ),
- 這不起作用,因為
-
LabelTarget label = Expression.Label(typeof(T));- 將上面的內容更改為:
LabelTarget breakLabel = Expression.Label("break");
- 將上面的內容更改為:
這是主要錯誤和無限回圈的原因:
Expression.Increment( index )上面確實增加
index了,但它不會將增加的值重新分配回index本地,所以它與在 C# 中執行此操作相同:while( true ) { if( index < first.Length ) { result = first[index] * second[index]; index 1; // <-- *DANGER, WILL ROBINSON!* } else { break; } }- 看到問題了嗎?做
index 1never 實際上增加index,所以index < first.Length總是如此,所以while回圈永遠不會停止。 - 解決方法是將其更改為
index = 1(或index或index),如下所示:Expression.PostIncrementAssign( index )
- 看到問題了嗎?做
最后一個問題是你最外層
Expression.Block的最后一個運算式應該是result本地的,相當于return result;在C#中做。- 因此,在您的呼叫站點內的呼叫之后,只需添加另一個引數即可。
Expression.Loop()Expression.Block( variables, expressions )result - 我承認我還沒有完全理解這個
breakLabel = Expression.Label("break");運算式是如何作業的,或者它甚至能做什么,但
- 因此,在您的呼叫站點內的呼叫之后,只需添加另一個引數即可。
此代碼在 .NET 6 中適用于我:
public static Func<T[], T[], T> GetVectorMultiplyFunction<T>()
where T : struct
{
var writeLineMethod = typeof(Console).GetMethod( nameof(Console.WriteLine), new[] { typeof(String), typeof(Object) })!; // print-style debugging ugh // `public static void WriteLine(string format, object? arg0)`
ParameterExpression first = Expression.Parameter( type: typeof(T[]) , name: "first" );
ParameterExpression second = Expression.Parameter( type: typeof(T[]) , name: "second" );
ParameterExpression result = Expression.Variable ( type: typeof(T) , name: "result" );
ParameterExpression index = Expression.Variable ( type: typeof(Int32), name: "index" );
LabelTarget breakLabel = Expression.Label("break");
BlockExpression block = Expression.Block(
variables : new[] { result, index },
expressions: new Expression[]
{
Expression.Assign( result, Expression.Constant( default(T) ) ),
Expression.Assign( index , Expression.Constant( 0 ) ),
Expression.Loop(
body: Expression.Block(
Expression.IfThenElse(
test : Expression.LessThan( index, Expression.ArrayLength( first ) ),
ifTrue: Expression.Block(
Expression.AddAssign( result, Expression.Multiply( Expression.ArrayIndex( first, index ), Expression.ArrayIndex( second, index) ) ),
Expression.PostIncrementAssign( index ),
Expression.Call( writeLineMethod, Expression.Constant( "result: {0}" ), Expression.Convert( result, typeof(Object) ) ),
Expression.Call( writeLineMethod, Expression.Constant( "index : {0}" ), Expression.Convert( index , typeof(Object) ) )
),
ifFalse: Expression.Break( breakLabel )
)
),
@break: breakLabel
),
result
}
);
Func<T[],T[],T> f = Expression
.Lambda< Func<T[],T[],T> >( block, first, second )
.Compile();
return f;
}
這是func回傳正確預期結果的螢屏截圖,以及帶有和Console.WriteLine的記錄值的輸出。該方法立即運行(在我的機器上呼叫也只用了 0.5ms):resultindexExpression.Lambda<>(...).Compile()

轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/510457.html
