1

我正在使用 Visual Studio 2017、C#、Windows 窗体为句子列表中的单词创建索引。

我有两个datagridview:

dataGridView2:这个网格有一个单列,其中每一行都包含一个带词的句子。

dGvTopics:这个网格对于在第一句(第一行)中重复的每个单词都有一列dataGridView2,列标题文本是单词

目标:我想单击按钮进行分类,为(句子)中dGvTopics的每一行插入一行dataGridView2,如果句子包含列标题文本,则将句子的副本作为该列的值。

我的代码是:

private void btnClassify_Click(object sender, EventArgs e)
{
    for (int i = 0; i < dGvTopics.Columns.Count; i++)
    {
        if (dataGridView2.Rows[i].Cells[0].Value.ToString().Contains(dGvTopics.Columns[i].HeaderText))
        {
            this.dGvTopics.Rows.Add();
            this.dGvTopics.Rows[i].Cells[i].Value = dataGridView2.Rows[i].Cells[0].Value;
        }
    }
}
4

2 回答 2

2

我们可以稍后讨论您为什么要这样做,有更简单的方法:)

您需要了解这里需要迭代两个维度,其中的行dataGridView2和 中的列dGvTopics,这意味着您将需要两个循环语句,而不仅仅是一个。

您当前的代码正在循环中的dataGridView2但仅针对其中的列数,dGvTopics这有点令人困惑。

专业提示: 不要使用没有意义的任意单字符变量名称。Yesi广泛用于表示您可以在网络上找到的代码中的索引,但这并不意味着它是一种好的做法。i应该为惰性编程保留,其中有一个您正在迭代的单维数组,在您的示例中,您访问了 4 个不同级别的数组,i现在的含义不明确。

代替i,使用有意义的变量名,如columnIndexor topicIndex。这样,当每一行都被单独审查时,代码就会更加自我记录。我什至会接受tc在这段代码中,从概念变量含义中获取第一个首字母将有助于发现错误索引器用于错误数组的常见错误。

是的,这使代码冗长而冗长,但我们并没有像我们的开发者祖先那样受到内存空间的限制,这不会改变最终可执行文件的大小,努力使您的代码自我记录。

如果您在代码内存受限的环境中编程,例如微控制器或微型芯片组,那么仍然使用有意义的短变量,而不是任意选择的字符。

应用上述建议突出了第一个问题:

for (int columnIndex = 0; columnIndex < dGvTopics.Columns.Count; columnIndex ++)
{
    if (dataGridView2.Rows[columnIndex].Cells[0].Value.ToString().Contains(dGvTopics.Columns[columnIndex].HeaderText))
    {
        this.dGvTopics.Rows.Add();
        this.dGvTopics.Rows[columnIndex].Cells[columnIndex].Value = dataGridView2.Rows[columnIndex].Cells[0].Value;
    }
}

现在我们可以看到,每次迭代都沿行向下移动,但以相同的速率跨单元格,这意味着只有对角线形式的单元格才会被比较并具有值。

下一个问题是,因为您仅在比较返回时创建一行true,这意味着其中的行dGvTopics可能少于您的预期,这意味着小于i(or columnIndex) 的值,这将引发下一次IndexOutOfRangeException成功的迭代任何失败的比较。

您可以通过分别迭代行和列并dGvTopicsdataGridView2. 我们还可以通过保存对 的引用currentSentence而不是通过数组索引器引用句子来使代码更清晰。

private void btnClassify_Click(object sender, EventArgs e)
{
    // remove any existing rows, we will reprocess all records.
    this.dGvTopics.Rows.Clear();

    // Iterate over the rows in the list of sentences.
    for (int rowIndex = 0; rowIndex < dataGridView2.Rows.Count; rowIndex ++)
    {
        // Create one topic row for every sentence
        // row index will always be valid now.
        this.dGvTopics.Rows.Add();

        // save the sentence value to simplify the comparison code.
        string currentSentence = dataGridView2.Rows[rowIndex].Cells[0].Value.ToString();
        
        // iterate over the columns in the topics grid
        for (int columnIndex = 0; columnIndex < dGvTopics.Columns.Count; columnIndex ++)
        {
            if (currentSentence.Contains(dGvTopics.Columns[columnIndex].HeaderText))
            {
                this.dGvTopics.Rows[rowIndex].Cells[columnIndex].Value = currentSentence;
            }
        }
    }
}

理解您为什么要这样做或如何使用这些信息并不容易。通常,对于操作单元格中的值,我们通常建议使用数据绑定技术,这样您就不再访问行和单元格,或者访问它们所代表的底层对象。

  • 证明这超出了这个问题的范围,但是当你有时间时,这是一个值得研究的途径。

在这样的解决方案中,有两个网格代表相同的逻辑组件,(在这种情况下,每个网格中的每一行代表相同的句子值)底层数据对象可能是一个列表,其中对象上的一个属性是句子和每个主题列都是同一对象的一个​​属性。

重要的是,使用数据绑定意味着需要使用您在网格中显示或编辑的信息的下一个过程可以在完全不访问或了解网格的情况下这样做......需要考虑的事情;)

更新

此代码可能会导致主题网格中出现许多空单元格。相反,我们可以只在需要时添加行,但要做到这一点需要更多的努力。

注意:网格呈现每一的所有单元格,在最后几行中,如果该行的至少一个单元格具有值,则可能仍然存在空单元格。

private void btnClassify_Click(object sender, EventArgs e)
{
    // remove any existing rows, we will reprocess all records.
    this.dGvTopics.Rows.Clear();

    // Iterate over the rows in the list of sentences.
    for (int rowIndex = 0; rowIndex < dataGridView2.Rows.Count; rowIndex ++)
    {
        // save the sentence value to simplify the comparison code.
        string currentSentence = dataGridView2.Rows[rowIndex].Cells[0].Value.ToString();

        // iterate over the columns in the topics grid
        for (int columnIndex = 0; columnIndex < dGvTopics.Columns.Count; columnIndex ++)
        {
            if (currentSentence.Contains(dGvTopics.Columns[columnIndex].HeaderText))
            {
                // first we need to know what row index to add this value into
                // that involves another iteration, we could store last index in another structure to make this quicker, but here we will do it from first principals.
                bool inserted = false;
                for(int lookupRow = 0; lookupRow < this.dGvTopics.Rows.Count; lookupRow ++)
                {
                    // find the first row with a null cell;
                    if(this.dGvTopics.Rows[columnIndex].Value == null)
                    {
                        this.dGvTopics.Rows[lookupRow].Cells[columnIndex].Value = currentSentence;
                        inserted = true;
                        break;
                    }
                }
                if(!inserted)
                {
                    this.dGvTopics.Rows.Add();
                    this.dGvTopics.Rows[this.dGvTopics.Rows.Count-1].Cells[columnIndex].Value = currentSentence;
                }
            }
        }
    }
}
于 2019-12-31T02:30:46.143 回答
1

非常感谢 Chris Schaller 先生,根据他的描述,最终代码编译后更改如下:

private void btnClassify_Click(object sender, EventArgs e)
{
    // remove any existing rows, we will reprocess all records.
    this.dGvTopics.Rows.Clear();

    // Iterate over the rows in the list of sentences.
    for (int rowIndex = 0; rowIndex < dataGridView2.Rows.Count; rowIndex++)
    {
        // save the sentence value to simplify the comparison code.
        string currentSentence = dataGridView2.Rows[rowIndex].Cells[0].Value.ToString();

        // iterate over the columns in the topics grid
        for (int columnIndex = 0; columnIndex < dGvTopics.Columns.Count; columnIndex++)
        {
            if (currentSentence.Contains(dGvTopics.Columns[columnIndex].HeaderText))
            {
                // first we need to know what row index to add this value into
                // that involves another iteration, we could store last index in another structure to make this quicker, but here we will do it from first principals.
                bool inserted = false;
                for (int lookupRow = 0; lookupRow < this.dGvTopics.Rows.Count; lookupRow++)
                {
                    // find the first row with a null cell;
                    if (this.dGvTopics.Rows[lookupRow].Cells[columnIndex].Value == null)
                    {
                        this.dGvTopics.Rows[lookupRow].Cells[columnIndex].Value = currentSentence;
                        inserted = true;
                        break;
                    }
                }
                if (!inserted)
                {
                    this.dGvTopics.Rows.Add();
                    this.dGvTopics.Rows[this.dGvTopics.Rows.Count - 1].Cells[columnIndex].Value = currentSentence;
                }
            }
        }
    }
}
于 2020-01-01T22:11:52.933 回答