由于您既是 C# 新手,又要求提供优雅的解决方案,我将举一个示例,说明如何使用更面向对象的方法来解决此问题。
首先,任何重要的“事物”都应该建模为一个类,即使它只有一个属性。这使得以后更容易扩展行为。您已经为 Roll 定义了一个类。我还要为成分添加一个类:
public class Ingredient
{
private string _name;
public string Name
{
get { return _name; }
}
public Ingredient(string name)
{
_name = name;
}
}
注意只有一个 getter 的 Name 属性,以及接受字符串 name 的构造函数。起初这可能看起来像不必要的复杂性,但会使您的代码更直接地在以后使用。
接下来,我们将根据本指南修改您的 Roll 类,并为其提供一些辅助方法,以便我们更轻松地检查卷是否包含某种(列表)成分:
public class Roll
{
private string _name;
private List<Ingredient> _ingredients = new List<Ingredient>();
public string Name
{
// By only exposing the property through a getter, you are preventing the name
// from being changed after the roll has been created
get { return _name; }
}
public List<Ingredient> Ingredients
{
// Similarly here, you are forcing the consumer to use the AddIngredient method
// where you can do any necessary checks before actually adding the ingredient
get { return _ingredients; }
}
public Roll(string name)
{
_name = name;
}
public bool AddIngredient(Ingredient ingredient)
{
// Returning a boolean value to indicate whether the ingredient was already present,
// gives the consumer of this class a way to present feedback to the end user
bool alreadyHasIngredient = _ingredients.Any(i => i.Name == ingredient.Name);
if (!alreadyHasIngredient)
{
_ingredients.Add(ingredient);
return true;
}
return false;
}
public bool ContainsIngredients(IEnumerable<Ingredient> ingredients)
{
// We use a method group to check for all of the supplied ingredients
// whether or not they exist
return ingredients.All(ContainsIngredient);
// Could be rewritten as: ingredients.All(i => ContainsIngredient(i));
}
public bool ContainsIngredient(Ingredient ingredient)
{
// We simply check if an ingredient is present by comparing their names
return _ingredients.Any(i => i.Name == ingredient.Name);
}
}
注意这里的 ContainsIngredient 和 ContainsIngredients 方法。现在您可以执行if (roll.ContainsIngredient(ingredient)) 之类的操作,这将使您的代码更具表现力和可读性。您将在我要添加的下一堂课中看到这一点,RollCollection。
您正在对可供选择的食物集合进行建模,大概是在餐厅菜单或某个类似领域的上下文中。您不妨继续建模:RollCollection。这将允许您在集合中封装一些有意义的逻辑。
同样,这类事情往往需要一些样板代码,一开始可能看起来过于复杂,但它会使您的类更易于使用。所以让我们添加一个 RollCollection:
public class RollCollection : IEnumerable<Roll>
{
private List<Roll> _rolls = new List<Roll>();
public RollCollection()
{
// We need to provide a default constructor if we want to be able
// to instantiate an empty RollCollection and then add rolls later on
}
public RollCollection(IEnumerable<Roll> rolls)
{
// By providing a constructor overload which accepts an IEnumerable<Roll>,
// we have the opportunity to create a new RollCollection based on a filtered existing collection of rolls
_rolls = rolls.ToList();
}
public RollCollection WhichContainIngredients(IEnumerable<Ingredient> ingredients)
{
IEnumerable<Roll> filteredRolls = _rolls
.Where(r => r.ContainsIngredients(ingredients));
return new RollCollection(filteredRolls);
}
public bool AddRoll(Roll roll)
{
// Similar to AddIngredient
bool alreadyContainsRoll = _rolls.Any(r => r.Name == roll.Name);
if (!alreadyContainsRoll)
{
_rolls.Add(roll);
return true;
}
return false;
}
#region IEnumerable implementation
public IEnumerator<Roll> GetEnumerator()
{
foreach (Roll roll in _rolls)
{
yield return roll;
}
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
#endregion
}
WhichContainIngredients 是我们真正在寻找的东西,因为它允许您执行以下操作:
// I have omitted the (proper) instantiation of Rolls and ChosenIngredients for brevity here
public RollCollection Rolls { get; set; }
public List<Ingredient> ChosenIngredients { get; set; }
public void Update()
{
Rolls = Rolls.WhichContainIngredients(ChosenIngredients);
}
这既简单又干净,正是您希望在表示层中做的事情。完成您的要求的逻辑现在很好地封装在 RollCollection 类中。
编辑:一个更完整(但仍然是简化)的示例,说明您的 Controller 类最终可能是什么样的:
public class Controller
{
private RollCollection _availableRolls = new RollCollection();
private List<Ingredient> _availableIngredients = new List<Ingredient>();
public RollCollection AvailableRolls
{
get { return _availableRolls; }
}
public List<Ingredient> AvailableIngredients
{
get { return _availableIngredients; }
}
public RollCollection RollsFilteredByIngredients
{
get { return AvailableRolls.WhichContainIngredients(ChosenIngredients); }
}
public List<Ingredient> ChosenIngredients { get; set; }
public Controller()
{
ChosenIngredients = new List<Ingredient>();
InitializeTestData();
}
private void InitializeTestData()
{
Ingredient ingredient1 = new Ingredient("Ingredient1");
Ingredient ingredient2 = new Ingredient("Ingredient2");
Ingredient ingredient3 = new Ingredient("Ingredient3");
_availableIngredients.Add(ingredient1);
_availableIngredients.Add(ingredient2);
_availableIngredients.Add(ingredient3);
Roll roll1 = new Roll("Roll1");
roll1.AddIngredient(ingredient1);
roll1.AddIngredient(ingredient2);
Roll roll2 = new Roll("Roll2");
roll2.AddIngredient(ingredient3);
_availableRolls.AddRoll(roll1);
_availableRolls.AddRoll(roll2);
}
}