我正在尝试将数字操作数表达式的字符串值(“GreaterThan”,“Equals”等)传递给参数。我创建了下面的代码,但是它很“笨重”。我不喜欢if
块,我认为有一种方法可以使用自定义LINQ比较谓词。我尝试按照 this post 中发布的回复,但我似乎无法遵循它。关于如何清理我的方法的任何想法?
这是代码,显示我想如何将字符串值“GreaterThan”传递给函数
var myValues = new Dictionary<string, int> {{"Foo", 1}, {"Bar", 6}};
var failed = DoAnyValuesFail(myValues, "GreaterThan", 4);
这是我写的“笨重”的示例方法:
public bool DoAnyValuesFail(Dictionary<string, int> dictionary, string expression, int failureValue)
{
var failureValues = new List<KeyValuePair<string, int>>();
if (expression == "GreaterThan")
failureValues = dictionary.Where(x => x.Value > failureValue).ToList();
if (expression == "LessThan")
failureValues = dictionary.Where(x => x.Value < failureValue).ToList();
if (expression == "Equals")
failureValues = dictionary.Where(x => x.Value == failureValue).ToList();
return failureValues.Any();
}
---更新 - 最终版本---
我认为下面的回答中的部分混淆是,我不能快速加速我的功能,谓词和代表的术语。对于那个很抱歉。无论如何,我确实想澄清一件事,那就是“GreaterThan”,“LessThan”和“Equals”的值来自配置文件,因此它们需要是在运行时调整的“Magic Strings”。
因此,根据Matthew Haugen和Enigmativity的反馈,我提出了以下代码,我认为这些代码最适合我的需求。如果您认为错误或需要调整,我愿意接受任何建议。
// These values actually come from a configuration file... shown here as hard coded just for illustration purposes
var failureValue = 2;
var numericQualifier = "<";
// This comes from my external data source
var myValues = new Dictionary<string, int> { { "Foo", 1 }, { "Bar", 6 } };
// This is the delegate (am I using that term correctly?) called Compare which is setup as an extension method
var failureValues = myValues.Where(x => numericQualifier.Compare()(x.Value, failureValue)).ToList();
if (failureValues.Any())
Console.WriteLine("The following values failed: {0}", string.Join(", ", failureValues));
这是我的Compare
扩展方法:
public static class MyExtensions
{
public static Func<int, int, bool> Compare(this string expression)
{
switch (expression)
{
case "GreaterThan":
case ">":
return (v, f) => v > f;
case "LessThan":
case "<":
return (v, f) => v < f;
case "Equals":
case "=":
return (v, f) => v == f;
default:
throw new ArgumentException(string.Format("The expression of '{0}' is invalid. Valid values are 'GreaterThan', 'LessThan' or 'Equals' or their respective symbols (>,<,=)", expression));
}
}
}
答案 0 :(得分:1)
我首先将它设为enum
而不是`字符串。
public enum ComparisonType
{
GreaterThan,
LessThan,
Equal,
}
然后,我会把它改成这样的东西。这也将提高性能,因为只需要一个匹配值即可返回。
public bool DoAnyValuesFail(Dictionary<string, int> dictionary, ComparisonType expression, int failureValue)
{
switch (expression)
{
case ComparisonType.Equals:
return dictionary.Any(x => x.Value == failureValue);
case ComparisonType.GreaterThan:
return dictionary.Any(x => x.Value > failureValue);
case ComparisonType.LessThan:
return dictionary.Any(x => x.Value < failureValue);
default:
throw new NotSupportedException();
}
}
当然,它并非所有比你所拥有的更清洁。它可能比依赖那些string
输入更可靠,这使它更具可读性。在我看来,并不是通过List<>
帮助。但我认为除此之外你还能做很多事情。我的意思是,您可以将Func<T, bool>
存储在switch
中分配的值中,然后再使用它,这会使return dictionary.Any(...)
正常化,但我觉得这样会降低可读性。
最终我觉得它很好。使用Expression
执行的任何操作都会使功能性的可读性变得如此简单。
答案 1 :(得分:1)
鉴于您需要将表达式与字符串匹配,我倾向于这样做:
private Dictionary<string, Func<int, int, bool>> _predicates =
new Dictionary<string, Func<int, int, bool>>
{
{ "GreaterThan", (v, f) => v > f },
{ "LessThan", (v, f) => v < f },
{ "Equals", (v, f) => v == f },
};
public bool DoAnyValuesFail(
Dictionary<string, int> dictionary,
string expression,
int failureValue)
{
return _predicates.ContainsKey(expression)
? dictionary.Any(kvp => _predicates[expression](kvp.Value, failureValue))
: false;
}
然而,正如其他人所说,我认为这是一个更好的选择:
public bool DoAnyValuesFail(
Dictionary<string, int> dictionary,
Func<int, bool> predicate)
{
return dictionary.Any(kvp => predicate(kvp.Value));
}
然后简单地称之为:
var failed = DoAnyValuesFail(myValues, x => x > 4);
但是,距离让它更简单只有一步之遥:
var failed = myValues.Any(x => x.Value > 4);
不需要DoAnyValuesFail
方法 - 意味着更简单的代码,更少的潜在错误,以及没有&#34;魔法&#34;字符串。
此代码更清晰,实际上比原始代码更简洁。
答案 2 :(得分:0)
您可以重写方法签名以使用这样的委托:
public bool DoAnyValuesFail(Dictionary<string, int> dictionary,Func<int,bool> predicate)
{
var failureValues = new List<KeyValuePair<string, int>>();
failureValues = dictionary.Where(x => predicate(x.Value)).ToList();
return failureValues.Any();
//instead of the code above you could simply do
//return dictionary.Any(x => predicate(x.Value));
}
然后当你调用它时,你提供了如下所需的表达式:
var myValues = new Dictionary<string, int> { { "Foo", 1 }, { "Bar", 6 } };
var failed = DoAnyValuesFail(myValues, x => x < 4); //4 is what you had as the failureValue