1

1つのオブジェクトをパラメーターとして受け取り、最終的にオブジェクトを返すLambdaExpressionがあります。テストの目的で、オブジェクトとしてボックス化されたDateTimeを返すLambda(実際に渡したものと一致するようにロングハンドで作成された)を次に示します。これを処理するために、LambdaExpressionはXmlNodeを受け取り、オブジェクトを返します。オブジェクトを返す必要があります。実際の戻りタイプは次のいずれかになります。DateTime、bool、int、decimal、XmlDocument [これまでのところ]一般的な考え方は、パーサーのどこかでこのラムダが作成され、そこから値を抽出するというものです。パラメータを入力し、入力されたものを返しますが、オブジェクトでボックス化されています。

     XmlNode node = null;
       ParameterExpression instanceExpression = Expression.Parameter(typeof(DynamicNode), "instance");
       ParameterExpression result = Expression.Parameter(typeof(object), "result");
       LabelTarget blockReturnLabel = Expression.Label(typeof(object));
       BlockExpression block = Expression.Block(
                             typeof(object),
                             new[] {  result },
                             Expression.Assign(result, Expression.Convert(Expression.Constant(DateTime.Now.AddSeconds(-1)), typeof(object))),
                             Expression.Return(blockReturnLabel, result),
                             Expression.Label(blockReturnLabel, Expression.Constant(-2, typeof(object))));
LambdaExpression lax = Expression.Lambda<Func<XmlNode, object>>(block, instanceExpression);

コードの後半で、<、<=、>、> =、==、および!=を評価しているため、このLambdaExpressionの結果を別の式と比較します。

通常、LambdaExpressionはExpression.LessThanの左側にあると想定できますが、ほとんどすべての式である可能性がありますが、型指定されていると想定します。つまり、ConstantExpressionまたは同様のものである可能性がありますが、タイプがあります。

これは、Expression.Invokeが呼び出されたときにLambdaExpressionがオブジェクトを返し、RHSがそのタイプであるため、Expression.LessThan[たとえば]が失敗することを意味します。

LambdaExpressionから返されたオブジェクト内にボックス化されたタイプが、実際には右側のタイプに匹敵すると仮定します。例えば

(object)5 < 6

ボックス化されたタイプとボックス化されていないタイプをクラッシュせずに比較できる式を作成するにはどうすればよいですか?私はlinqpadでさまざまな順列を試しました。これには、通常のc#でこれを書き込もうとすることも含まれます。つまり、式を使用せず、if-then-elseをネストするだけですが、正しく機能させることができませんでした。通常、私はおそらく次のようなものを書くでしょう:

/*
int i = 3;
object o = (object)i;
int compare = 4;
*/
DateTime dt = DateTime.Now;
object o = (object)dt;
DateTime compare = DateTime.Now.AddSeconds(1);

bool b = false;
if(o.GetType().IsAssignableFrom(compare.GetType()))
{
    if(o is int)
    {
        b = (int)o < (int)(object)compare;
    }
    if(o is DateTime)
    {
        b = (DateTime)o < (DateTime)(object)compare;
    }
    if(o is decimal)
    {
        b = (decimal)o < (decimal)(object)compare;
    }
}
Console.WriteLine(b);

これにより、oとcompareが実際には同じタイプであり、そのうちの1つがオブジェクトとしてボックス化されていると仮定すると、<操作を実行できます。

ですから、私の質問は、左側にLambdaExpressionがあり、右側にExpressionがある場合、上記のコードをどのように記述すればよいかということです[2つが同じタイプでない場合、結果としてfalseの方がクラッシュよりも優れています]

誰かが助けてくれることを願っています、

ガレス

4

2 に答える 2

1

ボックス化された型とボックス化されていない型をクラッシュせずに比較できる式を作成するにはどうすればよいですか?

Expression.Unboxこれには、「明示的なボックス化解除を表す UnaryExpression を作成します」というメソッドを使用できます 。

あなたの例を見てみましょう(int)(object)5 < 6

// boxed int
var left = Expression.Constant(5, typeof(object));

// int
var right = Expression.Constant(6, typeof(int));

// More generally, you can use right.Type  instead of typeof(int)
// if its static type is appropriate. 
// Otherwise, you may need to unbox it too.
var unboxedLeft = Expression.Unbox(left, typeof(int));

var lessThanEx = Expression.LessThan(unboxedLeft, right);    
var expression = Expression.Lambda<Func<bool>>(lessThanEx, null);

// True : (int)(object)5 < 6
bool b = expression.Compile()();

したがって、私の質問は、左側に LambdaExpression があり、右側に Expression がある場合、上記のコードをどのように記述すればよいかということです。[2 つが同じ型でない場合、結果として false の方が良い > クラッシュする]

この場合、ボックス化されたオブジェクトの実行時の型が右辺の型と同じかどうかをチェックする条件式を記述し、そうである場合はボックス化解除 + より小さい比較を行うことができます。それ以外の場合は false を返します。

例えば

// From earlier
var left =  ...
var right = ...
var lessThanEx = ...

var body = Expression.Condition(Expression.TypeEqual(left, right.Type), 
                                lessThanEx, 
                                Expression.Constant(false));

var expression = Expression.Lambda<Func<bool>>(body, null);
于 2011-02-25T11:15:17.157 に答える
1

私の元の投稿とあなたの回答を組み合わせる-これはLinqPadで機能するようです

XmlNode node = null;
ParameterExpression instanceExpression = Expression.Parameter(typeof(XmlNode), "instance");
ParameterExpression result = Expression.Parameter(typeof(object), "result");
LabelTarget blockReturnLabel = Expression.Label(typeof(object));
BlockExpression block = Expression.Block(
                     typeof(object),
                     new[] {  result },
                     //this would normally be a function invoke
                     Expression.Assign(result, Expression.Convert(Expression.Constant(DateTime.Now.AddSeconds(-1)), typeof(object))),
                     Expression.Return(blockReturnLabel, result),
                     Expression.Label(blockReturnLabel, Expression.Constant(-2, typeof(object))));
LambdaExpression lax = Expression.Lambda<Func<XmlNode, object>>(block, instanceExpression);

var left = Expression.Invoke(lax, instanceExpression);
//false result
//var right = Expression.Constant(5, typeof(int));  
//true result
var right = Expression.Constant(DateTime.Now, typeof(DateTime));

var unboxedLeft = Expression.Unbox(left, right.Type);  
var lessThanEx = Expression.LessThan(unboxedLeft, right);     

var body = Expression.Condition(Expression.TypeEqual(left, right.Type),  lessThanEx,  Expression.Constant(false));  
var expression = Expression.Lambda<Func<XmlNode, bool>>(body, instanceExpression); 

bool b = expression.Compile()(node); 
Console.WriteLine(b);
于 2011-02-25T11:55:31.157 に答える