C# 在C语言中,基于list对象包含的elementfrom另一个list的elements,我应该如何使用谓词逻辑从泛型list中删除元素?

C# 在C语言中,基于list对象包含的elementfrom另一个list的elements,我应该如何使用谓词逻辑从泛型list中删除元素?,c#,list,loops,predicate,C#,List,Loops,Predicate,我试图通过制作一个简单的程序来学习C语言,该程序向用户展示寿司卷所需的配料。i、 例如,一个用户想要一个带螃蟹的寿司卷,程序会列出一个包含螃蟹的寿司卷列表 我已经创建了一个Roll类 public class Roll { private string name; private List<string> ingredients = new List<string>(); } 战栗 我已经尝试了for循环,但是我似乎也无法让枚举工作,我想知道是否有必要

我试图通过制作一个简单的程序来学习C语言,该程序向用户展示寿司卷所需的配料。i、 例如,一个用户想要一个带螃蟹的寿司卷,程序会列出一个包含螃蟹的寿司卷列表

我已经创建了一个Roll类

public class Roll
{ 
    private string name;
    private List<string> ingredients = new List<string>();
}
战栗

我已经尝试了for循环,但是我似乎也无法让枚举工作,我想知道是否有必要只为这个方法枚举类

因此,我来到这里,试图找到一个优雅、专业的解决方案来解决我的问题。请记住,我是C新手,对类的谓词逻辑或枚举不太熟悉。

要使用RemoveAll,可以将条件重写为:

list.RemoveAll(roll => !ingredients.All(roll.checkForIngredient));
这利用了一个事实,即当编译器看到这一点时,它将有效地将其重写为:

list.RemoveAll(roll => !ingredients.All(i => roll.checkForIngredient(i)));
foreach(Roll roll in Rolls.ToList())
这就是你想要的。如果不是所有的成分都存在,则取出面包卷

话虽如此,既然你说你是一个初学者,如果你能让你的循环正常工作的话,也许你会觉得保持你的循环更舒服,也就是说,不要因为修改循环而崩溃。为此,只需创建集合的副本,然后在副本中循环,只需将foreach语句修改为:

list.RemoveAll(roll => !ingredients.All(i => roll.checkForIngredient(i)));
foreach(Roll roll in Rolls.ToList())
这将创建Rolls集合的基于列表的副本,然后在此基础上循环。列表不会被修改,即使Rolls是,它是一个单独的副本,包含创建时Rolls的所有元素

根据评论中的要求,我将尝试解释这一行代码是如何工作的:

list.RemoveAll(roll => !ingredients.All(roll.checkForIngredient));
RemoveAll方法,您可以看到它接受一个谓词,一个谓词,它基本上是一个委托,一个对方法的引用

这可以是lambda语法,使用=>运算符创建匿名方法。匿名方法基本上是一种在您想要使用它的地方声明的方法,没有名称,因此是匿名部分。让我们重写代码以使用匿名方法而不是lambda:

list.RemoveAll(delegate(Roll roll)
{
    return !ingredients.All(roll.checkForIngredient);
});
这是与上面lambda版本完全相同的编译代码,只是使用了匿名方法更详细的语法

那么,方法中的代码是如何工作的呢

All方法是在类上找到的扩展方法:

它将基本上循环遍历它所扩展的集合的所有元素,在本例中是单个卷的配料集合,并调用谓词函数。如果对于任何元素,谓词返回false,则调用All的结果也将为false。如果所有调用都返回true,则结果也将为true。请注意,如果集合成分为空,则结果也将为真

让我们试着重写我们的lambda代码,它又是这样的:

list.RemoveAll(roll => !ingredients.All(roll.checkForIngredient));
    // 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);
    }
转换为更详细的方法,而不是使用All扩展方法:

list.RemoveAll(delegate(Roll roll)
{
    bool all = true;
    foreach (var ingredient in ingredients)
        if (!roll.checkForIngredient(ingredient))
        {
            all = false;
            break;
        }

    return !all;
});
这现在开始看起来像您的原始代码,除了我们使用RemoveAll方法,它需要一个返回是否删除该项的谓词。因为如果全部为假,我们需要移除辊,我们使用not操作符!要反转该值

我试图通过制作一个简单的程序来学习C语言,向用户展示 寿司卷提供了他们想要的原料。i、 e.用户想要一个卷 和螃蟹一起,这个节目会列出一份寿司卷的清单 吃螃蟹

以下是我对给定问题的解决方案:

public class Roll
{
    public string Name { get; set; }
    private List<string> ingredients = new List<string>();
    public IList<string> Ingredients { get { return ingredients; } }

    public bool Contains(string ingredient)
    {
        return Ingredients.Any(i => i.Equals(ingredient));
    }
}
从我看到的情况来看,你试图从你的卷列表中删除所有不含螃蟹的卷。更好的方法是使用.Where过滤掉那些不包含crab的卷,如果需要直接操作整个列表,而不是一次遍历一个项目的集合,那么可以使用.ToList


您应该仔细阅读委托、迭代器、扩展方法和LINQ,以便更好地了解封面下的内容。

由于您都是C新手,但也要求提供一个优雅的解决方案,因此我将给您一个如何使用更面向对象的方法解决此问题的示例

首先,任何有意义的事物都应该被建模为一个类,即使它只有一个属性。这使得以后更容易扩展该行为。您已经为Roll定义了一个类。我还将为配料添加一个类:

public class Ingredient
{
    private string _name;

    public string Name
    {
        get { return _name; }
    }

    public Ingredient(string name)
    {
        _name = name;
    }
}
注意Name属性只有一个getter,构造函数接受一个字符串名。这在一开始看起来可能是不必要的复杂性,但会使您的代码更简单,以便以后进一步使用

接下来,我们将根据本指南修改您的Roll类,并为其提供一些帮助方法,使我们更容易检查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);
    }
}
其中包含的成分是我们真正想要的,因为它允许您执行以下操作:

list.RemoveAll(roll => !ingredients.All(roll.checkForIngredient));
    // 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类中

编辑:一个更完整但仍然简化的示例,展示了控制器类的最终外观:

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);
    }
}

因为这不是一个任务,我想做一个逻辑性最强、可读性最强的解决方案,所以请您详细说明list.RemoveAllroll=>是如何实现的!配料.Allroll.checkfor配料;作品?完成了,我已经解释了一点,希望有帮助,如果没有留下更多的评论:太好了!感谢您编写了一整段代码来解释参数是如何编译的:好东西!我一直想检查并清理代码,这将非常有帮助。这些是C语言普遍接受的命名约定吗?此外,此代码是否遵循MVC格式?我也在尽可能地保持这种风格。什么是可接受的命名约定实际上取决于你试图遵循的风格,更重要的是,如果你在团队中工作,你的团队已经同意了什么。我把我学到的一些习惯应用于我所学的领域驱动设计,我个人认为这是我在软件开发人员身上学到的最有用的东西。从这个角度来看,您的命名约定是编写代码,就好像非技术人员必须阅读和理解代码一样。您可能认为名称很长—如果这有助于代码的自解释性,那么这完全没关系。至于您对MVC的评论,这也是一个偏好问题,取决于您尝试遵循的其他设计模式或样式。就配料和辊类而言,它们完全可以在MVC中使用。RollCollection本身并不是一个真正的模型,更像是一个ViewModel。ViewModels并不是MVC规范的一部分,但它们当然可以而且可能应该与MVC规范结合使用。并不是因为它们太长,我只是不确定C开发人员喜欢如何命名它们。我会去阅读领域驱动的设计,这是一个很好的销售权。不管怎么说,这确实超出了我的想象,这已经证明是我在网站上发布的最有效的问题。^哈,C开发人员喜欢如何命名他们的东西和他们应该如何命名他们的东西之间可能有很大的区别。你应该以一种意图揭示的方式来命名你的东西,这样其他人就可以通过浏览你的变量和方法名来理解你在做什么,而不必阅读每一行代码来进行推理。我很高兴这对我有帮助!别忘了投票选出对你有帮助的答案/评论;