首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >这是MiscUtils中的一个bug吗?

这是MiscUtils中的一个bug吗?
EN

Stack Overflow用户
提问于 2013-11-22 06:05:56
回答 1查看 130关注 0票数 2

这是MiscUtils中的一个bug,还是我遗漏了什么?

代码语言:javascript
复制
decimal a = (1M/30);
int b = 59;
Assert.AreEqual(a*b, Operator.MultiplyAlternative(a, b));
Assert.AreEqual(b*a, Operator.MultiplyAlternative(b, a));

在最后一行失败:

代码语言:javascript
复制
expected: <1.9666666666666666666666666647>
 but was: <0>

更新

正如Petr所指出的,在CreateExpression方法中存在一些强制,导致了这个问题。在使用编译器时,我们不会看到这样的问题,因为参数以最高的精度被提升到类型,这也成为返回类型。

无论如何,第二个‘断言’应该失败了,因为如果与正常的C#行为一致,我希望它将第一个参数(b)提升到decimal并执行操作。通常,如果要将结果存储到精度较低的类型的变量中,则需要进行显式强制转换。但是,由于我们正在调用的方法的返回类型可能精度较低,而且调用方已经显式调用了返回精度较低的结果的方法--作为操作的一部分,自动执行潜在的截断强制转换似乎是合理的。换句话说,第二个表达式的预期结果将是1

因此,我们可以将CreateExpression更改为如下所示的行为:

代码语言:javascript
复制
if (castArgsToResultOnFailure && !(         // if we show retry                                                        
        typeof(TArg1) == typeof(TResult) &&  // and the args aren't
        typeof(TArg2) == typeof(TResult)))
{ // already "TValue, TValue, TValue"...
    var ltc = Type.GetTypeCode(lhs.Type);
    var rtc = Type.GetTypeCode(rhs.Type);
    // Use the higher precision element
    if (ltc > rtc)
    {
        // TArg1/TResult is higher precision than TArg2. Simply lift rhs
        var castRhs = Expression.Convert(rhs, lhs.Type);
        return
            Expression.Lambda<Func<TArg1, TArg2, TResult>>(body(lhs, castRhs), lhs, rhs).Compile();
    }
    // TArg2 is higher precision than TArg1/TResult. Lift lhs and Cast result
    var castLhs = Expression.Convert(lhs, rhs.Type);
    var castResult = Expression.Convert(body(castLhs, rhs), lhs.Type);
    return Expression.Lambda<Func<TArg1, TArg2, TResult>>(castResult, lhs, rhs).Compile();
}

因此,需要重写第二个断言:

代码语言:javascript
复制
Assert.AreEqual((int)(b*a), Operator.MultiplyAlternative(b, a));

现在这两种说法都成功了。如前所述,根据参数的顺序,将返回不同的结果,但现在第二次调用产生逻辑正确的结果。

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2013-11-22 06:17:33

根据源代码,方法签名如下:

代码语言:javascript
复制
public static TArg1 MultiplyAlternative<TArg1, TArg2>(TArg1 value1, TArg2 value2)

返回类型与第一个参数相同。因此,在第二种情况下,它使用int作为返回类型,并且很可能也会将第二个参数转换为该类型,因此您有59*0,它是零。

关于您的评论,源代码的这部分提供了详细信息:

代码语言:javascript
复制
/// <summary>
/// Create a function delegate representing a binary operation
/// </summary>
/// <param name="castArgsToResultOnFailure">
/// If no matching operation is possible, attempt to convert
/// TArg1 and TArg2 to TResult for a match? For example, there is no
/// "decimal operator /(decimal, int)", but by converting TArg2 (int) to
/// TResult (decimal) a match is found.
/// </param>
/// <typeparam name="TArg1">The first parameter type</typeparam>
/// <typeparam name="TArg2">The second parameter type</typeparam>
/// <typeparam name="TResult">The return type</typeparam>
/// <param name="body">Body factory</param>
/// <returns>Compiled function delegate</returns>
public static Func<TArg1, TArg2, TResult> CreateExpression<TArg1, TArg2, TResult>(
        Func<Expression, Expression, BinaryExpression> body, bool castArgsToResultOnFailure)
{
    ParameterExpression lhs = Expression.Parameter(typeof(TArg1), "lhs");
    ParameterExpression rhs = Expression.Parameter(typeof(TArg2), "rhs");
    try
    {
        try
        {
            return Expression.Lambda<Func<TArg1, TArg2, TResult>>(body(lhs, rhs), lhs, rhs).Compile();
        }
        catch (InvalidOperationException)
        {
            if (castArgsToResultOnFailure && !(         // if we show retry
                            typeof(TArg1) == typeof(TResult) &&  // and the args aren't
                            typeof(TArg2) == typeof(TResult)))
            { // already "TValue, TValue, TValue"...
                // convert both lhs and rhs to TResult (as appropriate)
                Expression castLhs = typeof(TArg1) == typeof(TResult) ?
                                (Expression)lhs :
                                (Expression)Expression.Convert(lhs, typeof(TResult));
                Expression castRhs = typeof(TArg2) == typeof(TResult) ?
                                (Expression)rhs :
                                (Expression)Expression.Convert(rhs, typeof(TResult));

                return Expression.Lambda<Func<TArg1, TArg2, TResult>>(
                        body(castLhs, castRhs), lhs, rhs).Compile();
            }
            else throw;
        }
    }
    catch (Exception ex)
    {
        string msg = ex.Message; // avoid capture of ex itself
        return delegate { throw new InvalidOperationException(msg); };
    }
}

您可以看到,Expression.Lambda<Func<TArg1, TArg2, TResult>>(body(lhs, rhs), lhs, rhs).Compile();对于TArg1TArg2 of intdecimal的任何组合都失败了(例如,如果您将decimal更改为double,那么它的行为实际上是相同的,因此它不仅受decimal影响),然后catch块试图将这两个参数都转换为TResult类型,而这又取决于参数顺序。所以在第一种情况下得到所有的decimal强制转换,在第二种情况下得到所有的int强制转换。

不幸的是,我不知道为什么这个lambda编译失败了,它是一个bug还是一个语言限制。

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

https://stackoverflow.com/questions/20138124

复制
相关文章

相似问题

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