2

I'm trying to prevent System.NullReferenceException.

I have a Company, which has a collection of Employees. Each Employee has a collection of Skills.

SelectedEmployee points to the currently selected element within the Employee collection.

SelectedSkill points to the currently selected element within the collection of Skills.

I have a ListView that has its ItemSource bound to the Skills collection; The ListView's SelectedItem is bound to the SelectedSkill.

When a Skill is deleted I want the ListView to scroll to the last element.

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

If an Employee has not been selected, then SelectedEmployee will be null. This will cause a System.NullReferenceException when doing anything inside the method.

NOTE: I've used an extension method to replace .Last() so it doesn't error on an empty collection.

To get around this I use a Util method:

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

So it now looks like this:

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

Everything above works fine. It's not the exact scenario, or code, so don't worry about correcting anything above (just assume it works fine). It's just the question below I'm really interested in finding out.

(Imagine for this that SelectedEmployee and SelectedSkill are not null)

Is there any way of getting the fully qualified name of a property? So I could do something like:

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

Where GetFullyQualifiedName(Object) returns "Company.SelectedEmployee.SelectedSkill".

Second part of the question: Imagine that SelectedEmployee is null: Is there any way of allowing a NullReference to be passed to a method? I'm 99.9% sure the answer is no :)

James
  • 93
  • 1
  • 5
  • 3
    why are you bothering with reflection? Why not simply check for `Company.SelectedEmployee != null`? – Jamiec Mar 15 '16 at 10:55
  • Imagine there are n number of parts to a property; any of which could be null. A.B.C.D.E.F.G.H.SelectedSkill. I don't want to have a huge If statement to check each one. – James Mar 16 '16 at 11:04

1 Answers1

7

I don't get it. Why not just:

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

Or in C#6

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

If you still want to have that GetFullyQualifiedName method, the nearest you could use could be something like (doesn't check for errors and it's just a quick hack):

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

Then use it like:

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

This would return a string containing "Foo.Bar.Baz.SomeProperty"

Check it in a Fiddle

Jcl
  • 27,696
  • 5
  • 61
  • 92
  • 1
    Probably should also check `Company.SelectedEmployee.Skills` just for completeness. – Jamiec Mar 15 '16 at 11:01
  • Thanks for the responses. I wasn't aware of the ? use within the if statement. However, as I mentioned, the code is just an example, the actual use would be for many more .selected.selected.selected.selected.. etc. I was trying to find a way of not having a huge If statement. Being able to find the fully qualified name for a property would be useful for me in several places. Any ideas? – James Mar 16 '16 at 11:01
  • @James there is no such thing as a "fully qualified name" for a property. There's an "assembly qualified name" for a type (but that includes the culture, version, etc.) and that's probably not what you are looking after. In any case, using the `?.` null propagation operand in C# 6, anything you do using strings will be more prone to refactoring errors and you will gain basically nothing (one `?` per dot is not a hassle) – Jcl Mar 16 '16 at 11:04
  • And for your second question, the answer is "no", no such thing exists, you could fake it using and parsing linq expressions (as in `GetNameForProperty(() => Property.SubProperty.ChildProperty)`, but again, not worth the hassle and runtime performance hit... in C#6 you can use `nameof(property)` but that only returns the name of the property/type/field, not its full "path" (if you want to call it so) – Jcl Mar 16 '16 at 11:07
  • Yeah, I thought so. Thanks. I'd been investigating it for a while and had assumed there wasn't a way, but I thought I'd throw it out there in case there was something cool I could do to grab it. I'll definitely be using the ?. to replace the reflection. – James Mar 16 '16 at 11:09
  • @James just for fun, I've made a very simple expression parser. Does not check for errors or anything, but I'm adding it to the question – Jcl Mar 16 '16 at 11:20
  • @James just added it, see if that suits your needs (I'd still prefer using `?.` , but there you go) – Jcl Mar 16 '16 at 11:24