C# 使用多个if条件筛选数据
我正在尝试使用参数条件名称对数据进行排序。 代码可以工作,但是读起来很糟糕,我想知道是否有更好的方法来编写它。 谢谢你的帮助C# 使用多个if条件筛选数据,c#,C#,我正在尝试使用参数条件名称对数据进行排序。 代码可以工作,但是读起来很糟糕,我想知道是否有更好的方法来编写它。 谢谢你的帮助 public async Task<ActionResult<IEnumerable<Product>>> FilterAsync(string conditionName) { var values = await GetProducts(); IOrderedEnumerable&l
public async Task<ActionResult<IEnumerable<Product>>> FilterAsync(string conditionName)
{
var values = await GetProducts();
IOrderedEnumerable<Product> dataSorted =null;
if (conditionName == "TitleASC")
{
dataSorted = values.OrderBy(c => c.Title);
}
if (conditionName == "TitleDESC")
{
dataSorted = values.OrderByDescending(c => c.CategoriesTitle);
}
if (conditionName == "DateTimeASC")
{
dataSorted =values.OrderBy(c => c.DateTime);
}
if (conditionName == "DateTimeDESC")
{
dataSorted = values.OrderByDescending(c => c.DateTime);
}
if (conditionName == "CategoryASC")
{
dataSorted = values.OrderBy(c => c.CategoriesTitle);
}
if (conditionName == "CategoryDESC")
{
dataSorted = values.OrderByDescending(c => c.CategoriesTitle);
}
return Ok(dataSorted);
}
公共异步任务FilterAsync(字符串条件名称)
{
var values=等待GetProducts();
IOrderedEnumerable dataSorted=null;
如果(条件名称==“TitleASC”)
{
dataSorted=values.OrderBy(c=>c.Title);
}
如果(条件名称=“标题ESC”)
{
dataSorted=values.OrderByDescending(c=>c.categoriestile);
}
如果(conditionName==“DateTimeASC”)
{
dataSorted=values.OrderBy(c=>c.DateTime);
}
如果(conditionName==“DateTimeDESC”)
{
dataSorted=values.OrderByDescending(c=>c.DateTime);
}
如果(条件名称=“类别”)
{
dataSorted=values.OrderBy(c=>c.categoriestile);
}
如果(条件名称==“CategoryDESC”)
{
dataSorted=values.OrderByDescending(c=>c.categoriestile);
}
返回Ok(数据排序);
}
switch语句或switch表达式在这里会有所帮助。并使您的代码更具可读性。您可以像这样简化您的解决方案:
public async Task<ActionResult<IEnumerable<Product>>> FilterAsync(string
conditionName)
{
var values = await GetProducts();
IOrderedEnumerable<Product> dataSorted =null;
switch (conditionName)
{
case "TitleASC":
dataSorted = values.OrderBy(c => c.Title);
break;
case "TitleDESC":
dataSorted = values.OrderByDescending(c => c.CategoriesTitle);
break;
case "DateTimeASC":
dataSorted =values.OrderBy(c => c.DateTime).ThenBy(c => c.CategoriesTitle);
break;
case "DateTimeDESC":
dataSorted = values.OrderByDescending(c => c.DateTime).ThenByDescending(c => c.CategoriesTitle);
break;
default:
// if you want you can add default order here
break;
}
return Ok(dataSorted);
}
公共异步任务FilterAsync(字符串
条件名称)
{
var values=等待GetProducts();
IOrderedEnumerable dataSorted=null;
开关(条件名称)
{
案例“TitleASC”:
dataSorted=values.OrderBy(c=>c.Title);
打破
案例“标题ESC”:
dataSorted=values.OrderByDescending(c=>c.categoriestile);
打破
案例“DateTimeASC”:
dataSorted=values.OrderBy(c=>c.DateTime);
打破
案例“DateTimeDESC”:
dataSorted=values.OrderByDescending(c=>c.DateTime),然后bydescending(c=>c.categoriestile);
打破
违约:
//如果需要,可以在此处添加默认订单
打破
}
返回Ok(数据排序);
}
开关表达式如何
公共异步任务FilterAsync(字符串条件名称)
{
var values=等待GetProducts();
IOrderedEnumerable dataSorted=条件名称开关
{
“TitleASC”=>values.OrderBy(c=>c.Title),
“TitleDESC”=>values.OrderByDescending(c=>c.categoriestile),
“DateTimeASC”=>values.OrderBy(c=>c.DateTime),
“DateTimeDESC”=>values.OrderByDescending(c=>c.DateTime),
“CategoryASC”=>values.OrderBy(c=>c.categoriestile),
“CategoryDESC”=>values.OrderByDescending(c=>c.categoriestile),
_=>空
};
返回Ok(数据排序);
}
一个选项是抽象功能。类似于
public async Task<ActionResult<IEnumerable<Product>>> FilterAsync(string conditionName)
{
var values = await GetProducts();
IOrderedEnumerable<Product> dataSorted =null;
val dataSorted = SortFunction(conditionName,values)
}
公共异步任务FilterAsync(字符串条件名称)
{
var values=等待GetProducts();
IOrderedEnumerable dataSorted=null;
val dataSorted=SortFunction(条件名称、值)
}
并让函数SortFunction执行逻辑。
在此函数中,对各种选项使用Case/switch语句。您可以使用上面建议的switch语句您可以使用switch
语句和拆分排序键和排序顺序来减少两倍的条件:
// Helper methods:
public static class LinqExtensions
{
public static IEnumerable<TSource> OrderByFlag<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector, bool descending = false)
{
return descending
? source.OrderByDescending(keySelector)
: source.OrderBy(keySelector);
}
}
// Then, in the controller:
public async Task<ActionResult<IEnumerable<Product>>> FilterAsync(string conditionName, bool desc = false)
{
var products = await GetProducts();
switch (conditionName)
{
case "Title":
products = products.OrderByFlag(p => p.Title, desc);
break;
case "DateTime":
products = products.OrderByFlag(p => p.DateTime, desc);
break;
case "Category":
products = products.OrderByFlag(p => p.CategoriesTitle, desc);
break;
default:
throw new ArgumentException(nameof(conditionName), "Unknown sorting criteria");
}
return Ok(products);
}
//帮助器方法:
公共静态类LinqExtensions
{
公共静态IEnumerable OrderByFlag(此IEnumerable源,Func keySelector,bool descending=false)
{
返回下降
?source.OrderByDescending(键选择器)
:source.OrderBy(keySelector);
}
}
//然后,在控制器中:
公共异步任务FilterAsync(字符串条件名称,bool desc=false)
{
var products=等待GetProducts();
开关(条件名称)
{
案例“标题”:
products=products.OrderByFlag(p=>p.Title,desc);
打破
案例“日期时间”:
products=products.OrderByFlag(p=>p.DateTime,desc);
打破
案例“类别”:
products=products.OrderByFlag(p=>p.categoriestile,desc);
打破
违约:
抛出新ArgumentException(nameof(conditionName),“未知排序条件”);
}
退货确认(产品);
}
您可以使用System.Linq.Dynamic.Core
int i = conditionName.Contains("DESC") ? conditionName.IndexOf("DESC") : conditionName.IndexOf("ASC");
//exp will be like Title desc
var exp = conditionName.Substring(0, i) + " " + conditionName.Substring(i);
var result = dataSorted.Orderby(exp).Tolist();
Dynamic允许您使用接受字符串参数的扩展方法来表示Linq查询。重要免责声明
在计算机科学中总是有一种权衡
我要建议的解决这个问题的方法需要提高解决方案的工程化水平这样做并不总是正确的方法,因为它增加了代码的抽象级别。
如果这段代码不会经常更改,并且可能的排序条件的数量很小,并且您认为它将保持很小的数量,那么您可以使用当前的实现(或者基于switch语句的等效解决方案,这可能在代码可读性方面更好)
为什么使用if级联是一个问题
除了代码可读性问题外,长串if
语句的主要问题是违反了开闭原则。你可以找到这个原理的解释
长话短说,我们的想法是,像您展示的那样的代码会不断变化,因为每次引入新条件(或删除现有条件)时,您都必须通过添加(或删除)新的if
语句来修改现有代码。这不是一个理想的场景,因为更改有效的现有代码可能会导致错误,而且从成本上讲,这是一项昂贵的操作
int i = conditionName.Contains("DESC") ? conditionName.IndexOf("DESC") : conditionName.IndexOf("ASC");
//exp will be like Title desc
var exp = conditionName.Substring(0, i) + " " + conditionName.Substring(i);
var result = dataSorted.Orderby(exp).Tolist();
// define an interface encapsualting the desired behavior
IProductsProvider
{
bool CanHandleCondition(string condition);
Task<IEnumerable<Products>> GetProducts(string condition);
}
// write an implementation of your interface for each possible condition that you have
class TitleAscendingProvider : IProductsProvider
{
private readonly IApiClient _apiClient;
public TitleAscendingProvider(IApiClient apiClient)
{
_apiClient = apiClient ?? throw new ArgumentNullException(nameof(apiClient));
}
public bool CanHandleCondition(string condition) => condition == "TitleASC";
public async Task<IEnumerable<Products>> GetProducts(string condition)
{
var products = await _apiClient.GetProducts();
return products.OrderBy(c => c.Title);
}
}
class TitleDescendingProvider : IProductsProvider
{
private readonly IApiClient _apiClient;
public TitleAscendingProvider(IApiClient apiClient)
{
_apiClient = apiClient ?? throw new ArgumentNullException(nameof(apiClient));
}
public bool CanHandleCondition(string condition) => condition == "TitleDESC";
public async Task<IEnumerable<Products>> GetProducts(string condition)
{
var products = await _apiClient.GetProducts();
return products.OrderByDescending(c => c.CategoriesTitle);
}
}
// this is the implementation of the interface that you will register with your DI container
// inject all the other implementations of the IProductsProvider interface
class CompositeProvider : IProductsProvider
{
private readonly IProductsProvider[] _providers;
public TitleAscendingProvider(IEnumerable<IProductsProvider> providers)
{
if (providers is null)
{
throw new ArgumentNullException(nameof(providers));
}
_providers = providers.ToArray();
}
public bool CanHandleCondition(string condition) => _providers.Any(p => p.CanHandleCondition(condition));
public Task<IEnumerable<Products>> GetProducts(string condition)
{
var provider = _providers.FirstOrDefault(p => p.CanHandleCondition(condition));
if (provider == null)
{
throw new InvalidOperationException("Unable to find a proper provider for the condition '{condition}'")
}
return provider.GetProducts(condition);
}
}
// remember to register the class CompositeProvider as the implementation of the interface IProductsProvider in the DI container
// let the DI container to inject the implementation of IProductsProvider in your controller
private readonly IProductsProvider _provider;
public async Task<ActionResult<IEnumerable<Product>>> FilterAsync(string conditionName)
{
var products = await _provider.GetProducts(conditionName);
return Ok(products);
}
static class Program
{
public static void Main(string[] args)
{
var people = new Person[]
{
new Person { Name = "Bob", Age = 11 },
new Person { Name = "Alice", Age = 8 },
new Person { Name = "Tony", Age = 1 }
};
//imagine this comes from the user...
var propertyName = nameof(Person.Age);
// the issue is that here you need to pass the right generic type argument and based on my understanding you can't infer it from the propertyName variable
var expression = GetPropertyExpression<int>(propertyName);
var func = expression.Compile();
var sorted = people.OrderBy(func).ToArray();
}
private static Expression<Func<Person, T>> GetPropertyExpression<T>(string propertyName)
{
var parameter = Expression.Parameter(typeof(Person), "model");
var property = Expression.Property(parameter, propertyName);
var expression = Expression.Lambda<Func<Person, T>>(property, parameter);
return expression;
}
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
}
}