C# 这是MiscUtils中的错误吗?
这是MiscUtils中的错误还是我遗漏了什么C# 这是MiscUtils中的错误吗?,c#,miscutils,C#,Miscutils,这是MiscUtils中的错误还是我遗漏了什么 decimal a = (1M/30); int b = 59; Assert.AreEqual(a*b, Operator.MultiplyAlternative(a, b)); Assert.AreEqual(b*a, Operator.MultiplyAlternative(b, a)); 在最后一行失败: expected: <1.9666666666666666666666666647> but was: <0>
decimal a = (1M/30);
int b = 59;
Assert.AreEqual(a*b, Operator.MultiplyAlternative(a, b));
Assert.AreEqual(b*a, Operator.MultiplyAlternative(b, a));
在最后一行失败:
expected: <1.9666666666666666666666666647>
but was: <0>
因此,第二个断言需要重写:
Assert.AreEqual((int)(b*a), Operator.MultiplyAlternative(b, a));
现在两种说法都成功了。与前面一样,根据参数的顺序,将返回不同的结果,但现在第二次调用将生成逻辑正确的结果。根据,方法签名如下所示:
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();
}
public static TArg1 MultiplyAlternative<TArg1, TArg2>(TArg1 value1, TArg2 value2)
你看,Expression.Lambda(body(lhs,rhs),lhs,rhs).Compile()对于int
和decimal
的TArg1
和TArg2
的任何组合,code>都会失败(例如,如果将decimal
更改为double
,它实际上具有相同的行为,因此它不仅会受到decimal
的影响),然后,catch
块试图将这两个参数强制转换为TResult
类型,而这又取决于参数顺序。所以在第一种情况下,所有的decimal
cast都被转换,在第二种情况下,所有的int
cast都被转换
不幸的是,我无法回答为什么这个lambda编译会失败,它是一个bug还是一个语言限制。C#这类东西太糟糕了。我使用这个库,它很棒,但不幸的是它不能解决所有的语言缺陷。是的,它是一个很棒的小库。但这件事让我大吃一惊,我不明白。如果TArg2是十进制的,为什么会强制为(int)0?在您的注释中添加了更多详细信息。谢谢你的帮助。
/// <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); };
}
}