关于Linq:如何修改此C#代码,以便Visual Studio识别出我不是白痴?

How can I modify this C# code so that Visual Studio recognizes that I'm not an idiot?

我有三条线

1
2
3
4
5
6
7
8
9
10
int selectedOrgId;

foreach (Organization o in PD.orgs)
    if (o.orgname == selectedOrgName)
         selectedOrgId = o.orgid;

PD.cats.InsertOnSubmit(new Category {
    orgid = selectedOrgId,
    catname = newCatName
});

在我的程序环境中,中间一行(循环)被保证为selectedOrgId设置一个值。但是,Visual Studio正在标记最后一行,因为

Use of unassigned local variable 'selectedOrgId'

除了

1
2
3
4
5
6
7
8
9
10
int selectedOrgId = 69;

foreach (Organization o in PD.orgs)
    if (o.orgname == selectedOrgName)
         selectedOrgId = o.orgid;

PD.cats.InsertOnSubmit(new Category {
    orgid = selectedOrgId,
    catname = newCatName
});

?????

虽然它是有效的,但它似乎是一个不雅的解决方案,因为它涉及一个神奇的数字。我想知道解决这个问题的正确方式。

编辑:

看看这里的讨论,我应该指定数据库中只有这样的orgid。我的foreach声明应该写得像

1
2
3
4
5
6
7
8
foreach (Organization o in PD.orgs)
{
    if (o.orgname == selectedOrgName)
    {
         selectedOrgId = o.orgid;
         break;
    }
 }

谢谢你给我介绍了一些更好地完成这整件事的方法!


您似乎在迭代一个集合,试图根据组织名称找到一个匹配的值。您可以使用Linq的SingleOrDefault()查找(最多)一个匹配项:

1
var selectedOrg = PD.orgs.SingleOrDefault(o => o.orgname == selectedOrgName);

然后,只有在找到值时才调用InsertOnSubmit()(否则,selectedorg将为空)

1
2
if (selectedOrg != null)
    PD.cats.InsertOnSubmit(new Category { orgid = selectedOrg.orgid, catname = newCatName });

不。

如果条件o.orgname == selectedOrgName不满足PD.orgs中的任何值,会发生什么?那么,selectedOrgId将保持未初始化状态。

但是,根据您的方法,以下代码可能更"优雅":

1
2
int selectedOrgId = PD.orgs.Single(o => o.orgname == selectedOrgName).orgid;
PD.cats.InsertOnSubmit(new Category { orgid = selectedOrgId, catname = newCatName });

请注意,您的代码将把selectedOrgId设置为它的最后一个实例,而我的代码将假定只有一个实例存在。


这不是唯一的问题。你知道你会找到一个或多个匹配项,编译器不会。

但"或更多"也是一个问题。代码只是不清楚你想要什么,这是根本原因。你有一个隐含的"最后一个胜利"策略。

当您使用一个更接近需求的解决方案时,编译器问题会消失,不会有任何黑客攻击。

没有LINQ:

1
2
3
4
5
6
7
8
9
10
11
12
13
// int selectedOrgId;

foreach (Organization o in PD.orgs)
    if (o.orgname == selectedOrgName)
    {
       PD.cats.InsertOnSubmit(new Category {
         orgid = o.orgid,
         catname = newCatName
      });
      return;   // or break;
    }
// shouldn't get here
throw new ...

和LINQ

1
2
3
4
5
Organization org =  PD.orgs.Single(o => o.orgname == selectedOrgName);
PD.cats.InsertOnSubmit(new Category {
          orgid = selectedOrgId,
          catname = newCatName
       });

Single(Predicate)方法与您的问题非常匹配。它将在结果集具有时引发!=1个元素。


编辑:op更改了问题,指定只找到一个项目,不需要多个解决方案,这里是选项1a。

方案1a-单线直线法

这提供了一个单行的LINQ查询,它将过滤掉不必要的组织,获取单个项目,并选择一个新的类别对象作为插入的参数。如果找不到单个项,则此方法将引发异常,但也就是说,根据您的问题应该发生什么。

1
2
3
4
5
PD.cats.InsertOnSubmit(
    PD.orgs.Where(o=>o.orgname==selectedOrgName)
    .Single()
    .Select(o=>new Category { orgid = o.orgId, catname = newCatName })
);

选项1b-迭代筛选列表并执行工作

这里的所有其他答案都建议使用LINQ,并假设只会找到一个记录。为什么不做循环中的所有事情,并使用LINQ过滤结果呢?

1
2
3
4
foreach (Organization o in PD.orgs.Where(o=>o.orgname==selectedOrgName))
{
    PD.cats.InsertOnSubmit(new Category { orgid = o.orgId, catname = newCatName });
}

这里的好处是没有if语句,它可以处理单个或多个案例。有一种方法可以在一行中进行此操作,并删除explicit for each和use list.for each(请参见比较):

1
2
PD.orgs.Where(o=>o.orgname==selectedOrgName).ToList()
.ForEach(o=>PD.cats.InsertOnSubmit(new Category { orgid = o.orgId, catname = newCatName }));

选项2-使用异常

这将使代码意图非常清楚,并让Visual Studio知道您已经处理好了这一点。我们的想法是让您的代码非常接近现在的状态:

1
2
3
4
5
6
int selectedOrgId;
foreach (Organization o in PD.orgs)
{
    if (o.orgname == selectedOrgName)
       selectedOrgId = o.orgid;
}

但是,此时我建议您使用一个例外,例如:

1
2
if(selectedOrgId == 0) throw new InvalidDataException("Selected Org Id cannot be 0");
PD.cats.InsertOnSubmit(new Category { orgid = selectedOrgId, catname = newCatName });


您可以使用int?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
int? selectedOrgId = null;

foreach (Organization o in PD.orgs)
{
    if (o.orgname == selectedOrgName)
    {
         selectedOrgId = o.orgid;
    }  
 }

 PD.cats.InsertOnSubmit(new Category
 {
        orgid = selectedOrgId,
        catname = newCatName
 });


下面的代码与您的代码所做的完全相同,但如果您没有匹配的组织(您认为不可能发生),则会引发异常:

1
2
3
4
PD.cats.InsertOnSubmit(new Category {
    orgid = PD.orgs.Last(o => o.orgname == selectedOrgName).orgid,
    catname = newCatName
});


使用linq选择带selectedOrgName的组织,得到orgid的组织:

1
2
3
4
PD.cats.InsertOnSubmit(new Category {
    orgid = PD.orgs.First(o => o.orgname == selectedOrgName).orgid,
    catname = newCatName
});

假设selectedOrgName总是在PD.orgs中,我是根据变量名进行假设的;但是,如果情况并非总是这样,您可以执行以下操作:

1
2
3
4
5
6
7
8
9
var selectedOrg = PD.orgs.FirstOrDefault(o => o.orgname == selectedOrgName);

if (selectedOrg != null)
{
    PD.cats.InsertOnSubmit(new Category {
        orgid = selectedOrg.orgid,
        catname = newCatName
    });
}