2016-03-15 115 views
0

我试图防止System.NullReferenceException。完全合格的属性名称

我有一个公司,其中有员工的集合。每个员工都有一系列技能。

SelectedEmployee指向Employee集合中当前选定的元素。

SelectedSkill指向技能集合中当前选定的元素。

我有一个ListView的ItemSource绑定到Skills集合; ListView的SelectedItem绑定到SelectedSkill。

当技能被删除时我希望ListView滚动到最后一个元素。

private void DeleteSelectedSkillFromSelectedEmployee() 
{ 
    Company.SelectedEmployee.Skills.Remove(Company.SelectedEmployee.SelectedSkill); 
    EmployeeSkillsListView.ScrollIntoView(Company.SelectedEmployee.Skills.Last()); 
} 

如果没有选择员工,则SelectedEmployee将为空。这将在方法内部执行任何操作时导致System.NullReferenceException。

注:我已经使用扩展方法来替换.Last(),因此它不会在空集合上出错。

为了解决这个问题我使用一个实用程序方法:

public static class Utils 
{ 
    public static bool PropertyExists(Object obj, String name) 
    { 
     foreach (String part in name.Split('.')) 
     { 
      if (obj == null) { return false; } 

      Type type = obj.GetType(); 
      System.Reflection.PropertyInfo info = type.GetProperty(part); 

      if (info == null) { return false; } 

      obj = info.GetValue(obj, null); 
     } 
     return obj != null; 
    } 
} 

所以它现在看起来是这样的:上面

private void DeleteSelectedSkillFromSelectedEmployee() 
{ 
    if(Utils.PropertyExists(Company, "SelectedEmployee.SelectedSkill")) 
    { 
     Company.SelectedEmployee.Skills.Remove(Company.SelectedEmployee.SelectedSkill); 
     EmployeeSkillsListView.ScrollIntoView(Company.SelectedEmployee.Skills.Last()); 
    } 
} 

,一切工作正常。这不是确切的场景或代码,所以不要担心纠正以上任何内容(只是假设它工作正常)。这只是我真正感兴趣的问题。

(想象一下,这是SelectedEmployee和SelectedSkill不为null)

有没有得到一个属性的完全合格的名称的方法吗? 所以,我可以这样做:

if(Utils.PropertyExists(Company, GetFullyQualifiedName(Company.SelectedEmployee.SelectedSkill))) 

凡GetFullyQualifiedName(Object)返回 “Company.SelectedEmployee.SelectedSkill”。

问题的第二部分:想象一下SelectedEmployee为null:是否有任何方法允许将NullReference传递给方法? 我99.9%肯定答案是否定的:)

+3

你为什么用反射烦心事字符串?为什么不简单检查'Company.SelectedEmployee!= null'? – Jamiec

+0

想象一下,一个属性有多少个零件;其中任何一个都可能为空。 A.B.C.D.E.F.G.H.SelectedSkill。我不想有一个巨大的If语句来检查每一个。 – James

回答

4

我不明白。为什么不干脆:

private void DeleteSelectedSkillFromSelectedEmployee() 
{ 
    if(Company != null && 
    Company.SelectedEmployee != null && 
    Company.SelectedEmployee.Skills != null) 
    {   
    Company.SelectedEmployee.Skills.Remove(Company.SelectedEmployee.SelectedSkill); 
    EmployeeSkillsListView.ScrollIntoView(Company.SelectedEmployee.Skills.Last()); 
    } 
} 

或者在C#6

if(Company?.SelectedEmployee?.Skills != null) 
{ 
    ... 
} 

如果你仍然想有GetFullyQualifiedName方法,你可以使用可能是像最近的(不检查错误,它只是一个快速的黑客):

public static string GetPathOfProperty<T>(Expression<Func<T>> property) 
{ 
    string resultingString = string.Empty; 
    var p = property.Body as MemberExpression; 
    while (p != null) 
    {    
    resultingString = p.Member.Name + (resultingString != string.Empty ? "." : "") + resultingString; 
    p = p.Expression as MemberExpression; 
    } 
    return resultingString;   
} 

然后使用它像:

GetPathOfProperty(() => Foo.Bar.Baz.SomeProperty); 

这将返回一个包含"Foo.Bar.Baz.SomeProperty"

Check it in a Fiddle

+1

也许还应该检查'Company.SelectedEmployee.Skills'是否完整。 – Jamiec

+0

感谢您的回复。我不知道?在if语句中使用。然而,正如我所提到的,代码仅仅是一个例子,实际使用将会更多.selected.selected.selected.selected ..等我试图找到一种没有一个巨大的If语句的方式。能够找到一个物业的完全合格的名称将在几个地方对我有用。有任何想法吗? – James

+0

@詹姆斯没有这样的东西作为一个属性的“完全合格的名称”。有一个类型的“组合限定名称”(但包括文化,版本等),这可能不是你正在寻找的。在任何情况下,使用C#6中的空传播操作数,你使用字符串的任何事情都会更容易出现重构错误,而且你基本上什么也得不到(每个点一个“?”并不麻烦) – Jcl